diff options
Diffstat (limited to 'security/keys')
| -rw-r--r-- | security/keys/Kconfig | 20 | ||||
| -rw-r--r-- | security/keys/big_key.c | 4 | ||||
| -rw-r--r-- | security/keys/encrypted-keys/ecryptfs_format.c | 3 | ||||
| -rw-r--r-- | security/keys/encrypted-keys/encrypted.c | 72 | ||||
| -rw-r--r-- | security/keys/gc.c | 4 | ||||
| -rw-r--r-- | security/keys/internal.h | 2 | ||||
| -rw-r--r-- | security/keys/key.c | 7 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 28 | ||||
| -rw-r--r-- | security/keys/keyctl_pkey.c | 14 | ||||
| -rw-r--r-- | security/keys/keyring.c | 5 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 2 | ||||
| -rw-r--r-- | security/keys/request_key.c | 2 | ||||
| -rw-r--r-- | security/keys/request_key_auth.c | 35 | ||||
| -rw-r--r-- | security/keys/trusted-keys/Kconfig | 36 | ||||
| -rw-r--r-- | security/keys/trusted-keys/Makefile | 2 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_caam.c | 111 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_core.c | 21 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_pkwm.c | 190 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_tee.c | 17 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_tpm1.c | 298 | ||||
| -rw-r--r-- | security/keys/trusted-keys/trusted_tpm2.c | 98 | ||||
| -rw-r--r-- | security/keys/user_defined.c | 4 |
22 files changed, 622 insertions, 353 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index abb03a1b2a5c..f4510d8cb485 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -3,7 +3,7 @@ # Key management configuration # -config KEYS +menuconfig KEYS bool "Enable access key retention support" select ASSOCIATIVE_ARRAY help @@ -21,9 +21,10 @@ config KEYS If you are unsure as to whether this is required, answer N. +if KEYS + config KEYS_REQUEST_CACHE bool "Enable temporary caching of the last request_key() result" - depends on KEYS help This option causes the result of the last successful request_key() call that didn't upcall to the kernel to be cached temporarily in the @@ -41,7 +42,6 @@ config KEYS_REQUEST_CACHE config PERSISTENT_KEYRINGS bool "Enable register of persistent per-UID keyrings" - depends on KEYS help This option provides a register of persistent per-UID keyrings, primarily aimed at Kerberos key storage. The keyrings are persistent @@ -58,9 +58,8 @@ config PERSISTENT_KEYRINGS config BIG_KEYS bool "Large payload keys" - depends on KEYS depends on TMPFS - depends on CRYPTO_LIB_CHACHA20POLY1305 = y + select CRYPTO_LIB_CHACHA20POLY1305 help This option provides support for holding large keys within the kernel (for example Kerberos ticket caches). The data may be stored out to @@ -70,7 +69,6 @@ config BIG_KEYS config TRUSTED_KEYS tristate "TRUSTED KEYS" - depends on KEYS help This option provides support for creating, sealing, and unsealing keys in the kernel. Trusted keys are random number symmetric keys, @@ -85,13 +83,10 @@ endif config ENCRYPTED_KEYS tristate "ENCRYPTED KEYS" - depends on KEYS select CRYPTO - select CRYPTO_HMAC select CRYPTO_AES select CRYPTO_CBC - select CRYPTO_SHA256 - select CRYPTO_RNG + select CRYPTO_LIB_SHA256 help This option provides support for create/encrypting/decrypting keys in the kernel. Encrypted keys are instantiated using kernel @@ -114,7 +109,6 @@ config USER_DECRYPTED_DATA config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" - depends on KEYS select CRYPTO select CRYPTO_KDF800108_CTR select CRYPTO_DH @@ -127,9 +121,11 @@ config KEY_DH_OPERATIONS config KEY_NOTIFICATIONS bool "Provide key/keyring change notifications" - depends on KEYS && WATCH_QUEUE + depends on WATCH_QUEUE help This option provides support for getting change notifications on keys and keyrings on which the caller has View permission. This makes use of pipes to handle the notification buffer and provides KEYCTL_WATCH_KEY to enable/disable watches. + +endif # KEYS diff --git a/security/keys/big_key.c b/security/keys/big_key.c index c3367622c683..268f702df380 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -66,7 +66,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data)); - if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) + if (datalen == 0 || datalen > 1024 * 1024 || !prep->data) return -EINVAL; /* Set an arbitrary quota */ @@ -103,7 +103,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) 0, enckey); /* save aligned data to file */ - file = shmem_kernel_file_setup("", enclen, 0); + file = shmem_kernel_file_setup("", enclen, EMPTY_VMA_FLAGS); if (IS_ERR(file)) { ret = PTR_ERR(file); goto err_enckey; diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c index 8fdd76105ce3..2fc6f3a66135 100644 --- a/security/keys/encrypted-keys/ecryptfs_format.c +++ b/security/keys/encrypted-keys/ecryptfs_format.c @@ -54,8 +54,7 @@ int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok, auth_tok->version = (((uint16_t)(major << 8) & 0xFF00) | ((uint16_t)minor & 0x00FF)); auth_tok->token_type = ECRYPTFS_PASSWORD; - strncpy((char *)auth_tok->token.password.signature, key_desc, - ECRYPTFS_PASSWORD_SIG_SIZE); + strscpy_pad(auth_tok->token.password.signature, key_desc); auth_tok->token.password.session_key_encryption_key_bytes = ECRYPTFS_MAX_KEY_BYTES; /* diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 831cb84fd75a..59cb77b237b3 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -13,6 +13,7 @@ #include <linux/uaccess.h> #include <linux/module.h> +#include <linux/hex.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/parser.h> @@ -27,7 +28,6 @@ #include <linux/scatterlist.h> #include <linux/ctype.h> #include <crypto/aes.h> -#include <crypto/hash.h> #include <crypto/sha2.h> #include <crypto/skcipher.h> #include <crypto/utils.h> @@ -37,8 +37,6 @@ static const char KEY_TRUSTED_PREFIX[] = "trusted:"; static const char KEY_USER_PREFIX[] = "user:"; -static const char hash_alg[] = "sha256"; -static const char hmac_alg[] = "hmac(sha256)"; static const char blkcipher_alg[] = "cbc(aes)"; static const char key_format_default[] = "default"; static const char key_format_ecryptfs[] = "ecryptfs"; @@ -54,8 +52,6 @@ static int blksize; #define MIN_DATA_SIZE 20 #define KEY_ENC32_PAYLOAD_LEN 32 -static struct crypto_shash *hash_tfm; - enum { Opt_new, Opt_load, Opt_update, Opt_err }; @@ -329,26 +325,6 @@ error: return ukey; } -static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, - const u8 *buf, unsigned int buflen) -{ - struct crypto_shash *tfm; - int err; - - tfm = crypto_alloc_shash(hmac_alg, 0, 0); - if (IS_ERR(tfm)) { - pr_err("encrypted_key: can't alloc %s transform: %ld\n", - hmac_alg, PTR_ERR(tfm)); - return PTR_ERR(tfm); - } - - err = crypto_shash_setkey(tfm, key, keylen); - if (!err) - err = crypto_shash_tfm_digest(tfm, buf, buflen, digest); - crypto_free_shash(tfm); - return err; -} - enum derived_key_type { ENC_KEY, AUTH_KEY }; /* Derive authentication/encryption key from trusted key */ @@ -357,7 +333,6 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, { u8 *derived_buf; unsigned int derived_buf_len; - int ret; derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen; if (derived_buf_len < HASH_SIZE) @@ -368,16 +343,15 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, return -ENOMEM; if (key_type) - strcpy(derived_buf, "AUTH_KEY"); + strscpy(derived_buf, "AUTH_KEY", HASH_SIZE); else - strcpy(derived_buf, "ENC_KEY"); + strscpy(derived_buf, "ENC_KEY", HASH_SIZE); memcpy(derived_buf + strlen(derived_buf) + 1, master_key, master_keylen); - ret = crypto_shash_tfm_digest(hash_tfm, derived_buf, derived_buf_len, - derived_key); + sha256(derived_buf, derived_buf_len, derived_key); kfree_sensitive(derived_buf); - return ret; + return 0; } static struct skcipher_request *init_skcipher_req(const u8 *key, @@ -503,10 +477,10 @@ static int datablob_hmac_append(struct encrypted_key_payload *epayload, goto out; digest = epayload->format + epayload->datablob_len; - ret = calc_hmac(digest, derived_key, sizeof derived_key, - epayload->format, epayload->datablob_len); - if (!ret) - dump_hmac(NULL, digest, HASH_SIZE); + hmac_sha256_usingrawkey(derived_key, sizeof(derived_key), + epayload->format, epayload->datablob_len, + digest); + dump_hmac(NULL, digest, HASH_SIZE); out: memzero_explicit(derived_key, sizeof(derived_key)); return ret; @@ -534,9 +508,8 @@ static int datablob_hmac_verify(struct encrypted_key_payload *epayload, } else p = epayload->format; - ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len); - if (ret < 0) - goto out; + hmac_sha256_usingrawkey(derived_key, sizeof(derived_key), p, len, + digest); ret = crypto_memneq(digest, epayload->format + epayload->datablob_len, sizeof(digest)); if (ret) { @@ -823,7 +796,7 @@ static int encrypted_instantiate(struct key *key, size_t datalen = prep->datalen; int ret; - if (datalen <= 0 || datalen > 32767 || !prep->data) + if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; datablob = kmalloc(datalen + 1, GFP_KERNEL); @@ -884,7 +857,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) if (key_is_negative(key)) return -ENOKEY; - if (datalen <= 0 || datalen > 32767 || !prep->data) + if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; buf = kmalloc(datalen + 1, GFP_KERNEL); @@ -1011,29 +984,14 @@ static int __init init_encrypted(void) { int ret; - hash_tfm = crypto_alloc_shash(hash_alg, 0, 0); - if (IS_ERR(hash_tfm)) { - pr_err("encrypted_key: can't allocate %s transform: %ld\n", - hash_alg, PTR_ERR(hash_tfm)); - return PTR_ERR(hash_tfm); - } - ret = aes_get_sizes(); if (ret < 0) - goto out; - ret = register_key_type(&key_type_encrypted); - if (ret < 0) - goto out; - return 0; -out: - crypto_free_shash(hash_tfm); - return ret; - + return ret; + return register_key_type(&key_type_encrypted); } static void __exit cleanup_encrypted(void) { - crypto_free_shash(hash_tfm); unregister_key_type(&key_type_encrypted); } diff --git a/security/keys/gc.c b/security/keys/gc.c index 7d687b0962b1..748e83818a76 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -218,8 +218,10 @@ continue_scanning: key = rb_entry(cursor, struct key, serial_node); cursor = rb_next(cursor); - if (refcount_read(&key->usage) == 0) + if (!test_bit_acquire(KEY_FLAG_USER_ALIVE, &key->flags)) { + /* Clobber key->user after final put seen. */ goto found_unreferenced_key; + } if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) { if (key->type == key_gc_dead_keytype) { diff --git a/security/keys/internal.h b/security/keys/internal.h index 2cffa6dc8255..b7b622bc36a1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *target, const void *callout_info, size_t callout_len, struct key *dest_keyring); +struct request_key_auth *request_key_auth_get(struct key *authkey); +void request_key_auth_put(struct request_key_auth *rka); extern struct key *key_get_instantiation_authkey(key_serial_t target_id); diff --git a/security/keys/key.c b/security/keys/key.c index 3d7d185019d3..b34a64d81d47 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -77,7 +77,7 @@ try_again: spin_unlock(&key_user_lock); user = NULL; - candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL); + candidate = kmalloc_obj(struct key_user); if (unlikely(!candidate)) goto out; @@ -298,6 +298,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->restrict_link = restrict_link; key->last_used_at = ktime_get_real_seconds(); + key->flags |= 1 << KEY_FLAG_USER_ALIVE; if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; if (flags & KEY_ALLOC_BUILT_IN) @@ -658,6 +659,8 @@ void key_put(struct key *key) key->user->qnbytes -= key->quotalen; spin_unlock_irqrestore(&key->user->lock, flags); } + /* Mark key as safe for GC after key->user done. */ + clear_bit_unlock(KEY_FLAG_USER_ALIVE, &key->flags); schedule_work(&key_gc_work); } } @@ -1272,7 +1275,7 @@ void __init key_init(void) { /* allocate a slab in which we can store keys */ key_jar = kmem_cache_create("key_jar", sizeof(struct key), - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_NO_MERGE, NULL); /* add the special key types */ list_add_tail(&key_type_keyring.link, &key_types_list); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ab927a142f51..d14ace88e529 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial_t id, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* pull the payload in if one was supplied */ payload = NULL; @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_t id, ret = -ENOMEM; payload = kvmalloc(plen, GFP_KERNEL); if (!payload) - goto error; + goto error_put_rka; ret = -EFAULT; if (!copy_from_iter_full(payload, plen, from)) @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_t id, error2: kvfree_sensitive(payload, plen); +error_put_rka: + request_key_auth_put(rka); error: return ret; } @@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (!instkey) goto error; - rka = instkey->payload.data[0]; - if (rka->target_key->serial != id) + rka = request_key_auth_get(instkey); + if (!rka) { + ret = -EKEYREVOKED; goto error; + } + if (rka->target_key->serial != id) + goto error_put_rka; /* find the destination keyring if present (which must also be * writable) */ ret = get_instantiation_keyring(ringid, rka, &dest_keyring); if (ret < 0) - goto error; + goto error_put_rka; /* instantiate the key and link it into a keyring */ ret = key_reject_and_link(rka->target_key, timeout, error, @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (ret == 0) keyctl_change_reqkey_auth(NULL); +error_put_rka: + request_key_auth_put(rka); error: return ret; } @@ -1796,13 +1808,13 @@ long keyctl_watch_key(key_serial_t id, int watch_queue_fd, int watch_id) if (watch_id >= 0) { ret = -ENOMEM; if (!key->watchers) { - wlist = kzalloc(sizeof(*wlist), GFP_KERNEL); + wlist = kzalloc_obj(*wlist); if (!wlist) goto err_wqueue; init_watch_list(wlist, NULL); } - watch = kzalloc(sizeof(*watch), GFP_KERNEL); + watch = kzalloc_obj(*watch); if (!watch) goto err_wlist; diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c index 97bc27bbf079..15b6bf6399b5 100644 --- a/security/keys/keyctl_pkey.c +++ b/security/keys/keyctl_pkey.c @@ -138,28 +138,35 @@ static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_par if (uparams.in_len > info.max_dec_size || uparams.out_len > info.max_enc_size) return -EINVAL; + + params->out_len = info.max_enc_size; break; case KEYCTL_PKEY_DECRYPT: if (uparams.in_len > info.max_enc_size || uparams.out_len > info.max_dec_size) return -EINVAL; + + params->out_len = info.max_dec_size; break; case KEYCTL_PKEY_SIGN: if (uparams.in_len > info.max_data_size || uparams.out_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; case KEYCTL_PKEY_VERIFY: if (uparams.in_len > info.max_data_size || uparams.in2_len > info.max_sig_size) return -EINVAL; + + params->out_len = info.max_sig_size; break; default: - BUG(); + return -EOPNOTSUPP; } params->in_len = uparams.in_len; - params->out_len = uparams.out_len; /* Note: same as in2_len */ return 0; } @@ -238,7 +245,8 @@ long keyctl_pkey_e_d_s(int op, params.op = kernel_pkey_sign; break; default: - BUG(); + ret = -EOPNOTSUPP; + goto error_params; } in = memdup_user(_in, params.in_len); diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f331725d5a37..7a2ee0ded7c9 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) struct keyring_search_context *ctx = iterator_data; const struct key *key = keyring_ptr_to_key(object); unsigned long kflags = READ_ONCE(key->flags); - short state = READ_ONCE(key->state); + short state = key_read_state(key); kenter("{%d}", key->serial); @@ -977,7 +977,7 @@ static struct key_restriction *keyring_restriction_alloc( key_restrict_link_func_t check) { struct key_restriction *keyres = - kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + kzalloc_obj(struct key_restriction); if (!keyres) return ERR_PTR(-ENOMEM); @@ -1109,6 +1109,7 @@ key_ref_t find_key_to_update(key_ref_t keyring_ref, kenter("{%d},{%s,%s}", keyring->serial, index_key->type->name, index_key->description); + guard(rcu)(); object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops, index_key); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index b5d5333ab330..a63c46bb2d14 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -51,7 +51,7 @@ static struct key *get_user_register(struct user_namespace *user_ns) if (!reg_keyring) { reg_keyring = keyring_alloc(".user_reg", user_ns->owner, INVALID_GID, - &init_cred, + kernel_cred(), KEY_POS_WRITE | KEY_POS_SEARCH | KEY_USR_VIEW | KEY_USR_READ, 0, diff --git a/security/keys/request_key.c b/security/keys/request_key.c index a7673ad86d18..fa2bb9f2f538 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -332,7 +332,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring) case KEY_REQKEY_DEFL_GROUP_KEYRING: default: - BUG(); + return -EINVAL; } /* diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 8f33cd170e42..282e09d8fa46 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *); static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char *, size_t); +static void request_key_auth_rcu_disposal(struct rcu_head *); /* * The request-key authorisation key type definition. @@ -116,6 +117,31 @@ static void free_request_key_auth(struct request_key_auth *rka) } /* + * Take a reference to the request-key authorisation payload so callers can + * drop authkey->sem before doing operations that may sleep. + */ +struct request_key_auth *request_key_auth_get(struct key *authkey) +{ + struct request_key_auth *rka; + + down_read(&authkey->sem); + rka = dereference_key_locked(authkey); + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags)) + refcount_inc(&rka->usage); + else + rka = NULL; + up_read(&authkey->sem); + + return rka; +} + +void request_key_auth_put(struct request_key_auth *rka) +{ + if (rka && refcount_dec_and_test(&rka->usage)) + call_rcu(&rka->rcu, request_key_auth_rcu_disposal); +} + +/* * Dispose of the request_key_auth record under RCU conditions */ static void request_key_auth_rcu_disposal(struct rcu_head *rcu) @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key) struct request_key_auth *rka = dereference_key_locked(key); kenter("{%d}", key->serial); + if (!rka) + return; rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } /* @@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key) kenter("{%d}", key->serial); if (rka) { rcu_assign_keypointer(key, NULL); - call_rcu(&rka->rcu, request_key_auth_rcu_disposal); + request_key_auth_put(rka); } } @@ -171,9 +199,10 @@ struct key *request_key_auth_new(struct key *target, const char *op, kenter("%d,", target->serial); /* allocate a auth record */ - rka = kzalloc(sizeof(*rka), GFP_KERNEL); + rka = kzalloc_obj(*rka); if (!rka) goto error; + refcount_set(&rka->usage, 1); rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL); if (!rka->callout_info) goto error_free_rka; diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig index 1fb8aa001995..e5a4a53aeab2 100644 --- a/security/keys/trusted-keys/Kconfig +++ b/security/keys/trusted-keys/Kconfig @@ -1,14 +1,32 @@ config HAVE_TRUSTED_KEYS bool +config HAVE_TRUSTED_KEYS_DEBUG + bool + +config TRUSTED_KEYS_DEBUG + bool "Debug trusted keys" + depends on HAVE_TRUSTED_KEYS_DEBUG + default n + help + Trusted key backends and core code that support debug traces can + opt-in that feature here. Traces must only use debug level output, as + sensitive data may pass by. In the kernel-command line traces can be + enabled via trusted.dyndbg='+p'. + + SAFETY: Debug dumps are inactive at runtime until trusted.debug is set + to a true value on the kernel command-line. Use at your utmost + consideration when enabling this feature on a production build. The + general advice is not to do this. + config TRUSTED_KEYS_TPM bool "TPM-based trusted keys" depends on TCG_TPM >= TRUSTED_KEYS default y - select CRYPTO - select CRYPTO_HMAC - select CRYPTO_SHA1 + select HAVE_TRUSTED_KEYS_DEBUG select CRYPTO_HASH_INFO + select CRYPTO_LIB_SHA1 + select CRYPTO_LIB_UTILS select ASN1_ENCODER select OID_REGISTRY select ASN1 @@ -24,6 +42,7 @@ config TRUSTED_KEYS_TEE bool "TEE-based trusted keys" depends on TEE >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of the Trusted Execution Environment (TEE) as trusted @@ -34,6 +53,7 @@ config TRUSTED_KEYS_CAAM depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS select CRYPTO_DEV_FSL_CAAM_BLOB_GEN default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of NXP's Cryptographic Accelerator and Assurance Module @@ -43,10 +63,20 @@ config TRUSTED_KEYS_DCP bool "DCP-based trusted keys" depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS_DEBUG select HAVE_TRUSTED_KEYS help Enable use of NXP's DCP (Data Co-Processor) as trusted key backend. +config TRUSTED_KEYS_PKWM + bool "PKWM-based trusted keys" + depends on PSERIES_PLPKS >= TRUSTED_KEYS + default y + select HAVE_TRUSTED_KEYS_DEBUG + select HAVE_TRUSTED_KEYS + help + Enable use of IBM PowerVM Key Wrapping Module (PKWM) as a trusted key backend. + if !HAVE_TRUSTED_KEYS comment "No trust source selected!" endif diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile index f0f3b27f688b..5fc053a21dad 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -16,3 +16,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o + +trusted-$(CONFIG_TRUSTED_KEYS_PKWM) += trusted_pkwm.o diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c index e3415c520c0a..6a33dbf2a7f5 100644 --- a/security/keys/trusted-keys/trusted_caam.c +++ b/security/keys/trusted-keys/trusted_caam.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de> + * Copyright 2025 NXP */ #include <keys/trusted_caam.h> #include <keys/trusted-type.h> #include <linux/build_bug.h> #include <linux/key-type.h> +#include <linux/parser.h> #include <soc/fsl/caam-blob.h> static struct caam_blob_priv *blobifier; @@ -16,6 +18,80 @@ static struct caam_blob_priv *blobifier; static_assert(MAX_KEY_SIZE + CAAM_BLOB_OVERHEAD <= CAAM_BLOB_MAX_LEN); static_assert(MAX_BLOB_SIZE <= CAAM_BLOB_MAX_LEN); +enum { + opt_err, + opt_key_enc_algo, +}; + +static const match_table_t key_tokens = { + {opt_key_enc_algo, "key_enc_algo=%s"}, + {opt_err, NULL} +}; + +#ifdef CONFIG_TRUSTED_KEYS_DEBUG +static inline void dump_options(const struct caam_pkey_info *pkey_info) +{ + if (!trusted_debug) + return; + + pr_debug("key encryption algo %d\n", pkey_info->key_enc_algo); +} +#else +static inline void dump_options(const struct caam_pkey_info *pkey_info) +{ +} +#endif + +static int get_pkey_options(char *c, + struct caam_pkey_info *pkey_info) +{ + substring_t args[MAX_OPT_ARGS]; + unsigned long token_mask = 0; + u16 key_enc_algo; + char *p = c; + int token; + int res; + + if (!c) + return 0; + + while ((p = strsep(&c, " \t"))) { + if (*p == '\0' || *p == ' ' || *p == '\t') + continue; + token = match_token(p, key_tokens, args); + if (test_and_set_bit(token, &token_mask)) + return -EINVAL; + + switch (token) { + case opt_key_enc_algo: + res = kstrtou16(args[0].from, 16, &key_enc_algo); + if (res < 0) + return -EINVAL; + pkey_info->key_enc_algo = key_enc_algo; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static bool is_key_pkey(char **datablob) +{ + char *c = NULL; + + do { + /* Second argument onwards, + * determine if tied to HW + */ + c = strsep(datablob, " \t"); + if (c && (strcmp(c, "pk") == 0)) + return true; + } while (c); + + return false; +} + static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob) { int ret; @@ -25,11 +101,30 @@ static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob) .key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1, }; + /* + * If it is to be treated as protected key, + * read next arguments too. + */ + if (is_key_pkey(&datablob)) { + info.pkey_info.plain_key_sz = p->key_len; + info.pkey_info.is_pkey = 1; + ret = get_pkey_options(datablob, &info.pkey_info); + if (ret < 0) + return 0; + dump_options(&info.pkey_info); + } + ret = caam_encap_blob(blobifier, &info); if (ret) return ret; p->blob_len = info.output_len; + if (info.pkey_info.is_pkey) { + p->key_len = p->blob_len + sizeof(struct caam_pkey_info); + memcpy(p->key, &info.pkey_info, sizeof(struct caam_pkey_info)); + memcpy(p->key + sizeof(struct caam_pkey_info), p->blob, p->blob_len); + } + return 0; } @@ -42,11 +137,27 @@ static int trusted_caam_unseal(struct trusted_key_payload *p, char *datablob) .key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1, }; + if (is_key_pkey(&datablob)) { + info.pkey_info.plain_key_sz = p->blob_len - CAAM_BLOB_OVERHEAD; + info.pkey_info.is_pkey = 1; + ret = get_pkey_options(datablob, &info.pkey_info); + if (ret < 0) + return 0; + dump_options(&info.pkey_info); + + p->key_len = p->blob_len + sizeof(struct caam_pkey_info); + memcpy(p->key, &info.pkey_info, sizeof(struct caam_pkey_info)); + memcpy(p->key + sizeof(struct caam_pkey_info), p->blob, p->blob_len); + + return 0; + } + ret = caam_decap_blob(blobifier, &info); if (ret) return ret; p->key_len = info.output_len; + return 0; } diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index e2d9644efde1..0509d9955f2a 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -12,8 +12,10 @@ #include <keys/trusted_caam.h> #include <keys/trusted_dcp.h> #include <keys/trusted_tpm.h> +#include <keys/trusted_pkwm.h> #include <linux/capability.h> #include <linux/err.h> +#include <linux/hex.h> #include <linux/init.h> #include <linux/key-type.h> #include <linux/module.h> @@ -29,9 +31,15 @@ static char *trusted_rng = "default"; module_param_named(rng, trusted_rng, charp, 0); MODULE_PARM_DESC(rng, "Select trusted key RNG"); +#ifdef CONFIG_TRUSTED_KEYS_DEBUG +bool trusted_debug; +module_param_named(debug, trusted_debug, bool, 0); +MODULE_PARM_DESC(debug, "Enable trusted keys debug traces (default: 0)"); +#endif + static char *trusted_key_source; module_param_named(source, trusted_key_source, charp, 0); -MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)"); +MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam, dcp or pkwm)"); static const struct trusted_key_source trusted_key_sources[] = { #if defined(CONFIG_TRUSTED_KEYS_TPM) @@ -46,6 +54,9 @@ static const struct trusted_key_source trusted_key_sources[] = { #if defined(CONFIG_TRUSTED_KEYS_DCP) { "dcp", &dcp_trusted_key_ops }, #endif +#if defined(CONFIG_TRUSTED_KEYS_PKWM) + { "pkwm", &pkwm_trusted_key_ops }, +#endif }; DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal); @@ -54,7 +65,7 @@ DEFINE_STATIC_CALL_NULL(trusted_key_unseal, DEFINE_STATIC_CALL_NULL(trusted_key_get_random, *trusted_key_sources[0].ops->get_random); static void (*trusted_key_exit)(void); -static unsigned char migratable; +static unsigned char migratable __ro_after_init; enum { Opt_err, @@ -129,7 +140,7 @@ static struct trusted_key_payload *trusted_payload_alloc(struct key *key) ret = key_payload_reserve(key, sizeof(*p)); if (ret < 0) goto err; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc_obj(*p); if (!p) goto err; @@ -157,7 +168,7 @@ static int trusted_instantiate(struct key *key, int key_cmd; size_t key_len; - if (datalen <= 0 || datalen > 32767 || !prep->data) + if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL); @@ -240,7 +251,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) p = key->payload.data[0]; if (!p->migratable) return -EPERM; - if (datalen <= 0 || datalen > 32767 || !prep->data) + if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL); diff --git a/security/keys/trusted-keys/trusted_pkwm.c b/security/keys/trusted-keys/trusted_pkwm.c new file mode 100644 index 000000000000..bf42c6679245 --- /dev/null +++ b/security/keys/trusted-keys/trusted_pkwm.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 IBM Corporation, Srish Srinivasan <ssrish@linux.ibm.com> + */ + +#include <keys/trusted_pkwm.h> +#include <keys/trusted-type.h> +#include <linux/build_bug.h> +#include <linux/key-type.h> +#include <linux/parser.h> +#include <asm/plpks.h> + +enum { + Opt_err, + Opt_wrap_flags, +}; + +static const match_table_t key_tokens = { + {Opt_wrap_flags, "wrap_flags=%s"}, + {Opt_err, NULL} +}; + +static int getoptions(char *datablob, struct trusted_key_options *opt) +{ + substring_t args[MAX_OPT_ARGS]; + char *p = datablob; + int token; + int res; + u16 wrap_flags; + unsigned long token_mask = 0; + struct trusted_pkwm_options *pkwm; + + if (!datablob) + return 0; + + pkwm = opt->private; + + while ((p = strsep(&datablob, " \t"))) { + if (*p == '\0' || *p == ' ' || *p == '\t') + continue; + + token = match_token(p, key_tokens, args); + if (test_and_set_bit(token, &token_mask)) + return -EINVAL; + + switch (token) { + case Opt_wrap_flags: + res = kstrtou16(args[0].from, 16, &wrap_flags); + if (res < 0 || wrap_flags > 2) + return -EINVAL; + pkwm->wrap_flags = wrap_flags; + break; + default: + return -EINVAL; + } + } + return 0; +} + +static struct trusted_key_options *trusted_options_alloc(void) +{ + struct trusted_key_options *options; + struct trusted_pkwm_options *pkwm; + + options = kzalloc_obj(*options); + + if (options) { + pkwm = kzalloc_obj(*pkwm); + + if (!pkwm) { + kfree_sensitive(options); + options = NULL; + } else { + options->private = pkwm; + } + } + + return options; +} + +static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob) +{ + struct trusted_key_options *options = NULL; + struct trusted_pkwm_options *pkwm = NULL; + u8 *input_buf, *output_buf; + u32 output_len, input_len; + int rc; + + options = trusted_options_alloc(); + + if (!options) + return -ENOMEM; + + rc = getoptions(datablob, options); + if (rc < 0) + goto out; + dump_options(options); + + input_len = p->key_len; + input_buf = kmalloc(ALIGN(input_len, 4096), GFP_KERNEL); + if (!input_buf) { + pr_err("Input buffer allocation failed. Returning -ENOMEM."); + rc = -ENOMEM; + goto out; + } + + memcpy(input_buf, p->key, p->key_len); + + pkwm = options->private; + + rc = plpks_wrap_object(&input_buf, input_len, pkwm->wrap_flags, + &output_buf, &output_len); + if (!rc) { + memcpy(p->blob, output_buf, output_len); + p->blob_len = output_len; + dump_payload(p); + } else { + pr_err("Wrapping of payload key failed: %d\n", rc); + } + + kfree(input_buf); + kfree(output_buf); + +out: + kfree_sensitive(options->private); + kfree_sensitive(options); + return rc; +} + +static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob) +{ + u8 *input_buf, *output_buf; + u32 input_len, output_len; + int rc; + + input_len = p->blob_len; + input_buf = kmalloc(ALIGN(input_len, 4096), GFP_KERNEL); + if (!input_buf) { + pr_err("Input buffer allocation failed. Returning -ENOMEM."); + return -ENOMEM; + } + + memcpy(input_buf, p->blob, p->blob_len); + + rc = plpks_unwrap_object(&input_buf, input_len, &output_buf, + &output_len); + if (!rc) { + memcpy(p->key, output_buf, output_len); + p->key_len = output_len; + dump_payload(p); + } else { + pr_err("Unwrapping of payload failed: %d\n", rc); + } + + kfree(input_buf); + kfree(output_buf); + + return rc; +} + +static int trusted_pkwm_init(void) +{ + int ret; + + if (!plpks_wrapping_is_supported()) { + pr_err("H_PKS_WRAP_OBJECT interface not supported\n"); + return -ENODEV; + } + + ret = plpks_gen_wrapping_key(); + if (ret) { + pr_err("Failed to generate default wrapping key\n"); + return -EINVAL; + } + + return register_key_type(&key_type_trusted); +} + +static void trusted_pkwm_exit(void) +{ + unregister_key_type(&key_type_trusted); +} + +struct trusted_key_ops pkwm_trusted_key_ops = { + .migratable = 0, /* non-migratable */ + .init = trusted_pkwm_init, + .seal = trusted_pkwm_seal, + .unseal = trusted_pkwm_unseal, + .exit = trusted_pkwm_exit, +}; diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c index aa3d477de6db..6e465c8bef5e 100644 --- a/security/keys/trusted-keys/trusted_tee.c +++ b/security/keys/trusted-keys/trusted_tee.c @@ -202,9 +202,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) return 0; } -static int trusted_key_probe(struct device *dev) +static int trusted_key_probe(struct tee_client_device *rng_device) { - struct tee_client_device *rng_device = to_tee_client_device(dev); + struct device *dev = &rng_device->dev; int ret; struct tee_ioctl_open_session_arg sess_arg; @@ -244,13 +244,11 @@ out_ctx: return ret; } -static int trusted_key_remove(struct device *dev) +static void trusted_key_remove(struct tee_client_device *dev) { unregister_key_type(&key_type_trusted); tee_client_close_session(pvt_data.ctx, pvt_data.session_id); tee_client_close_context(pvt_data.ctx); - - return 0; } static const struct tee_client_device_id trusted_key_id_table[] = { @@ -261,23 +259,22 @@ static const struct tee_client_device_id trusted_key_id_table[] = { MODULE_DEVICE_TABLE(tee, trusted_key_id_table); static struct tee_client_driver trusted_key_driver = { + .probe = trusted_key_probe, + .remove = trusted_key_remove, .id_table = trusted_key_id_table, .driver = { .name = DRIVER_NAME, - .bus = &tee_bus_type, - .probe = trusted_key_probe, - .remove = trusted_key_remove, }, }; static int trusted_tee_init(void) { - return driver_register(&trusted_key_driver.driver); + return tee_client_driver_register(&trusted_key_driver); } static void trusted_tee_exit(void) { - driver_unregister(&trusted_key_driver.driver); + tee_client_driver_unregister(&trusted_key_driver); } struct trusted_key_ops trusted_key_tee_ops = { diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 89c9798d1800..13513819991e 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -7,6 +7,9 @@ */ #include <crypto/hash_info.h> +#include <crypto/sha1.h> +#include <crypto/utils.h> +#include <linux/hex.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/parser.h> @@ -14,78 +17,98 @@ #include <linux/err.h> #include <keys/trusted-type.h> #include <linux/key-type.h> -#include <linux/crypto.h> -#include <crypto/hash.h> -#include <crypto/sha1.h> #include <linux/tpm.h> #include <linux/tpm_command.h> #include <keys/trusted_tpm.h> -static const char hmac_alg[] = "hmac(sha1)"; -static const char hash_alg[] = "sha1"; static struct tpm_chip *chip; static struct tpm_digest *digests; -struct sdesc { - struct shash_desc shash; - char ctx[]; +/* implementation specific TPM constants */ +#define TPM_SIZE_OFFSET 2 +#define TPM_RETURN_OFFSET 6 +#define TPM_DATA_OFFSET 10 + +#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset])) +#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset]) +#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset])) + +struct osapsess { + uint32_t handle; + unsigned char secret[SHA1_DIGEST_SIZE]; + unsigned char enonce[TPM_NONCE_SIZE]; }; -static struct crypto_shash *hashalg; -static struct crypto_shash *hmacalg; +/* discrete values, but have to store in uint16_t for TPM use */ +enum { + SEAL_keytype = 1, + SRK_keytype = 4 +}; -static struct sdesc *init_sdesc(struct crypto_shash *alg) +#ifdef CONFIG_TRUSTED_KEYS_DEBUG +static inline void dump_options(struct trusted_key_options *o) { - struct sdesc *sdesc; - int size; - - size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); - sdesc = kmalloc(size, GFP_KERNEL); - if (!sdesc) - return ERR_PTR(-ENOMEM); - sdesc->shash.tfm = alg; - return sdesc; + 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); + print_hex_dump_debug("pcrinfo ", DUMP_PREFIX_NONE, + 16, 1, o->pcrinfo, o->pcrinfo_len, 0); } -static int TSS_sha1(const unsigned char *data, unsigned int datalen, - unsigned char *digest) +static inline void dump_sess(struct osapsess *s) { - struct sdesc *sdesc; - int ret; + if (!trusted_debug) + return; + + print_hex_dump_debug("trusted-key: handle ", DUMP_PREFIX_NONE, + 16, 1, &s->handle, 4, 0); + pr_debug("secret:\n"); + print_hex_dump_debug("", DUMP_PREFIX_NONE, + 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); + pr_debug("trusted-key: enonce:\n"); + print_hex_dump_debug("", DUMP_PREFIX_NONE, + 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); +} - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } +static inline void dump_tpm_buf(unsigned char *buf) +{ + int len; - ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); - kfree_sensitive(sdesc); - return ret; + if (!trusted_debug) + return; + pr_debug("\ntpm buffer\n"); + len = LOAD32(buf, TPM_SIZE_OFFSET); + print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); } +#else +static inline void dump_options(struct trusted_key_options *o) +{ +} + +static inline void dump_sess(struct osapsess *s) +{ +} + +static inline void dump_tpm_buf(unsigned char *buf) +{ +} +#endif static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, unsigned int keylen, ...) { - struct sdesc *sdesc; + struct hmac_sha1_ctx hmac_ctx; va_list argp; unsigned int dlen; unsigned char *data; - int ret; - - sdesc = init_sdesc(hmacalg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc %s\n", hmac_alg); - return PTR_ERR(sdesc); - } + int ret = 0; - ret = crypto_shash_setkey(hmacalg, key, keylen); - if (ret < 0) - goto out; - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; + hmac_sha1_init_usingrawkey(&hmac_ctx, key, keylen); va_start(argp, keylen); for (;;) { @@ -97,46 +120,34 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, ret = -EINVAL; break; } - ret = crypto_shash_update(&sdesc->shash, data, dlen); - if (ret < 0) - break; + hmac_sha1_update(&hmac_ctx, data, dlen); } va_end(argp); if (!ret) - ret = crypto_shash_final(&sdesc->shash, digest); -out: - kfree_sensitive(sdesc); + hmac_sha1_final(&hmac_ctx, digest); return ret; } /* * calculate authorization info fields to send to TPM */ -int TSS_authhmac(unsigned char *digest, const unsigned char *key, +static int TSS_authhmac(unsigned char *digest, const unsigned char *key, unsigned int keylen, unsigned char *h1, unsigned char *h2, unsigned int h3, ...) { unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct sha1_ctx sha_ctx; unsigned int dlen; unsigned char *data; unsigned char c; - int ret; + int ret = 0; va_list argp; if (!chip) return -ENODEV; - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - c = !!h3; - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; + sha1_init(&sha_ctx); va_start(argp, h3); for (;;) { dlen = va_arg(argp, unsigned int); @@ -147,27 +158,22 @@ int TSS_authhmac(unsigned char *digest, const unsigned char *key, ret = -EINVAL; break; } - ret = crypto_shash_update(&sdesc->shash, data, dlen); - if (ret < 0) - break; + sha1_update(&sha_ctx, data, dlen); } va_end(argp); if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); + sha1_final(&sha_ctx, paramdigest); if (!ret) ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, paramdigest, TPM_NONCE_SIZE, h1, TPM_NONCE_SIZE, h2, 1, &c, 0, 0); -out: - kfree_sensitive(sdesc); return ret; } -EXPORT_SYMBOL_GPL(TSS_authhmac); /* * verify the AUTH1_COMMAND (Seal) result from TPM */ -int TSS_checkhmac1(unsigned char *buffer, +static int TSS_checkhmac1(unsigned char *buffer, const uint32_t command, const unsigned char *ononce, const unsigned char *key, @@ -182,7 +188,7 @@ int TSS_checkhmac1(unsigned char *buffer, unsigned char *authdata; unsigned char testhmac[SHA1_DIGEST_SIZE]; unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct sha1_ctx sha_ctx; unsigned int dlen; unsigned int dpos; va_list argp; @@ -203,51 +209,30 @@ int TSS_checkhmac1(unsigned char *buffer, continueflag = authdata - 1; enonce = continueflag - TPM_NONCE_SIZE; - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); - if (ret < 0) - goto out; + sha1_init(&sha_ctx); + sha1_update(&sha_ctx, (const u8 *)&result, sizeof(result)); + sha1_update(&sha_ctx, (const u8 *)&ordinal, sizeof(ordinal)); va_start(argp, keylen); for (;;) { dlen = va_arg(argp, unsigned int); if (dlen == 0) break; dpos = va_arg(argp, unsigned int); - ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) - break; + sha1_update(&sha_ctx, buffer + dpos, dlen); } va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); - if (ret < 0) - goto out; + sha1_final(&sha_ctx, paramdigest); ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, 1, continueflag, 0, 0); if (ret < 0) - goto out; + return ret; - if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) - ret = -EINVAL; -out: - kfree_sensitive(sdesc); - return ret; + if (crypto_memneq(testhmac, authdata, SHA1_DIGEST_SIZE)) + return -EINVAL; + return 0; } -EXPORT_SYMBOL_GPL(TSS_checkhmac1); /* * verify the AUTH2_COMMAND (unseal) result from TPM @@ -273,7 +258,7 @@ static int TSS_checkhmac2(unsigned char *buffer, unsigned char testhmac1[SHA1_DIGEST_SIZE]; unsigned char testhmac2[SHA1_DIGEST_SIZE]; unsigned char paramdigest[SHA1_DIGEST_SIZE]; - struct sdesc *sdesc; + struct sha1_ctx sha_ctx; unsigned int dlen; unsigned int dpos; va_list argp; @@ -296,22 +281,9 @@ static int TSS_checkhmac2(unsigned char *buffer, enonce1 = continueflag1 - TPM_NONCE_SIZE; enonce2 = continueflag2 - TPM_NONCE_SIZE; - sdesc = init_sdesc(hashalg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc %s\n", hash_alg); - return PTR_ERR(sdesc); - } - ret = crypto_shash_init(&sdesc->shash); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, - sizeof result); - if (ret < 0) - goto out; - ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, - sizeof ordinal); - if (ret < 0) - goto out; + sha1_init(&sha_ctx); + sha1_update(&sha_ctx, (const u8 *)&result, sizeof(result)); + sha1_update(&sha_ctx, (const u8 *)&ordinal, sizeof(ordinal)); va_start(argp, keylen2); for (;;) { @@ -319,42 +291,33 @@ static int TSS_checkhmac2(unsigned char *buffer, if (dlen == 0) break; dpos = va_arg(argp, unsigned int); - ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) - break; + sha1_update(&sha_ctx, buffer + dpos, dlen); } va_end(argp); - if (!ret) - ret = crypto_shash_final(&sdesc->shash, paramdigest); - if (ret < 0) - goto out; + sha1_final(&sha_ctx, paramdigest); ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, paramdigest, TPM_NONCE_SIZE, enonce1, TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0); if (ret < 0) - goto out; - if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { - ret = -EINVAL; - goto out; - } + return ret; + if (crypto_memneq(testhmac1, authdata1, SHA1_DIGEST_SIZE)) + return -EINVAL; ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, paramdigest, TPM_NONCE_SIZE, enonce2, TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0); if (ret < 0) - goto out; - if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) - ret = -EINVAL; -out: - kfree_sensitive(sdesc); - return ret; + return ret; + if (crypto_memneq(testhmac2, authdata2, SHA1_DIGEST_SIZE)) + return -EINVAL; + return 0; } /* * For key specific tpm requests, we will generate and send our * own TPM command packets using the drivers send function. */ -int trusted_tpm_send(unsigned char *cmd, size_t buflen) +static int trusted_tpm_send(unsigned char *cmd, size_t buflen) { struct tpm_buf buf; int rc; @@ -380,7 +343,6 @@ int trusted_tpm_send(unsigned char *cmd, size_t buflen) tpm_put_ops(chip); return rc; } -EXPORT_SYMBOL_GPL(trusted_tpm_send); /* * Lock a trusted key, by extending a selected PCR. @@ -434,7 +396,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, /* * Create an object independent authorisation protocol (oiap) session */ -int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) +static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) { int ret; @@ -451,7 +413,6 @@ int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) TPM_NONCE_SIZE); return 0; } -EXPORT_SYMBOL_GPL(oiap); struct tpm_digests { unsigned char encauth[SHA1_DIGEST_SIZE]; @@ -485,7 +446,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, int i; /* alloc some work space for all the hashes */ - td = kmalloc(sizeof *td, GFP_KERNEL); + td = kmalloc_obj(*td); if (!td) return -ENOMEM; @@ -498,9 +459,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, /* calculate encrypted authorization value */ memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); - ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); - if (ret < 0) - goto out; + sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); if (ret < 0) @@ -885,7 +844,7 @@ static struct trusted_key_options *trusted_options_alloc(void) if (tpm2 < 0) return NULL; - options = kzalloc(sizeof *options, GFP_KERNEL); + options = kzalloc_obj(*options); if (options) { /* set any non-zero defaults */ options->keytype = SRK_keytype; @@ -989,46 +948,11 @@ static int trusted_tpm_get_random(unsigned char *key, size_t key_len) return tpm_get_random(chip, key, key_len); } -static void trusted_shash_release(void) -{ - if (hashalg) - crypto_free_shash(hashalg); - if (hmacalg) - crypto_free_shash(hmacalg); -} - -static int __init trusted_shash_alloc(void) -{ - int ret; - - hmacalg = crypto_alloc_shash(hmac_alg, 0, 0); - if (IS_ERR(hmacalg)) { - pr_info("could not allocate crypto %s\n", - hmac_alg); - return PTR_ERR(hmacalg); - } - - hashalg = crypto_alloc_shash(hash_alg, 0, 0); - if (IS_ERR(hashalg)) { - pr_info("could not allocate crypto %s\n", - hash_alg); - ret = PTR_ERR(hashalg); - goto hashalg_fail; - } - - return 0; - -hashalg_fail: - crypto_free_shash(hmacalg); - return ret; -} - static int __init init_digests(void) { int i; - digests = kcalloc(chip->nr_allocated_banks, sizeof(*digests), - GFP_KERNEL); + digests = kzalloc_objs(*digests, chip->nr_allocated_banks); if (!digests) return -ENOMEM; @@ -1049,15 +973,10 @@ static int __init trusted_tpm_init(void) ret = init_digests(); if (ret < 0) goto err_put; - ret = trusted_shash_alloc(); - if (ret < 0) - goto err_free; ret = register_key_type(&key_type_trusted); if (ret < 0) - goto err_release; + goto err_free; return 0; -err_release: - trusted_shash_release(); err_free: kfree(digests); err_put: @@ -1070,7 +989,6 @@ static void trusted_tpm_exit(void) if (chip) { put_device(&chip->dev); kfree(digests); - trusted_shash_release(); unregister_key_type(&key_type_trusted); } } diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 024be262702f..6340823f8b53 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -18,14 +18,6 @@ #include "tpm2key.asn1.h" -static struct tpm2_hash tpm2_hash_map[] = { - {HASH_ALGO_SHA1, TPM_ALG_SHA1}, - {HASH_ALGO_SHA256, TPM_ALG_SHA256}, - {HASH_ALGO_SHA384, TPM_ALG_SHA384}, - {HASH_ALGO_SHA512, TPM_ALG_SHA512}, - {HASH_ALGO_SM3_256, TPM_ALG_SM3_256}, -}; - static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 }; static int tpm2_key_encode(struct trusted_key_payload *payload, @@ -244,20 +236,13 @@ int tpm2_seal_trusted(struct tpm_chip *chip, off_t offset = TPM_HEADER_SIZE; struct tpm_buf buf, sized; int blob_len = 0; - u32 hash; + int hash; u32 flags; - int i; int rc; - for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { - if (options->hash == tpm2_hash_map[i].crypto_id) { - hash = tpm2_hash_map[i].tpm_id; - break; - } - } - - if (i == ARRAY_SIZE(tpm2_hash_map)) - return -EINVAL; + hash = tpm2_find_hash_alg(options->hash); + if (hash < 0) + return hash; if (!options->keyhandle) return -EINVAL; @@ -283,7 +268,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out_put; } - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + if (rc) + goto out; + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, options->keyauth, TPM_DIGEST_SIZE); @@ -331,7 +319,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - tpm_buf_fill_hmac_session(chip, &buf); + rc = tpm_buf_fill_hmac_session(chip, &buf); + if (rc) + goto out; + rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (rc) @@ -348,25 +339,19 @@ int tpm2_seal_trusted(struct tpm_chip *chip, } blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len); + if (blob_len < 0) + rc = blob_len; out: tpm_buf_destroy(&sized); tpm_buf_destroy(&buf); - if (rc > 0) { - if (tpm2_rc_value(rc) == TPM2_RC_HASH) - rc = -EINVAL; - else - rc = -EPERM; - } - if (blob_len < 0) - rc = blob_len; - else + if (!rc) payload->blob_len = blob_len; out_put: tpm_put_ops(chip); - return rc; + return tpm_ret_to_err(rc); } /** @@ -387,6 +372,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, struct trusted_key_options *options, u32 *blob_handle) { + u8 *blob_ref __free(kfree) = NULL; struct tpm_buf buf; unsigned int private_len; unsigned int public_len; @@ -400,6 +386,9 @@ static int tpm2_load_cmd(struct tpm_chip *chip, /* old form */ blob = payload->blob; payload->old_format = 1; + } else { + /* Bind for cleanup: */ + blob_ref = blob; } /* new format carries keyhandle but old format doesn't */ @@ -444,7 +433,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip, return rc; } - tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + if (rc) + goto out; + tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth, TPM_DIGEST_SIZE); @@ -456,7 +448,10 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - tpm_buf_fill_hmac_session(chip, &buf); + rc = tpm_buf_fill_hmac_session(chip, &buf); + if (rc) + goto out; + rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (!rc) @@ -464,18 +459,13 @@ static int tpm2_load_cmd(struct tpm_chip *chip, (__be32 *) &buf.data[TPM_HEADER_SIZE]); out: - if (blob != payload->blob) - kfree(blob); tpm_buf_destroy(&buf); - if (rc > 0) - rc = -EPERM; - - return rc; + return tpm_ret_to_err(rc); } /** - * tpm2_unseal_cmd() - execute a TPM2_Unload command + * tpm2_unseal_cmd() - execute a TPM2_Unseal command * * @chip: TPM chip to use * @payload: the key data in clear and encrypted form @@ -491,8 +481,10 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, struct trusted_key_options *options, u32 blob_handle) { + struct tpm_header *head; struct tpm_buf buf; u16 data_len; + int offset; u8 *data; int rc; @@ -506,7 +498,9 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, return rc; } - tpm_buf_append_name(chip, &buf, blob_handle, NULL); + rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL); + if (rc) + goto out; if (!options->policyhandle) { tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, @@ -527,15 +521,22 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, tpm2_buf_append_auth(&buf, options->policyhandle, NULL /* nonce */, 0, 0, options->blobauth, options->blobauth_len); - tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT, - NULL, 0); + if (tpm2_chip_auth(chip)) { + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0); + } else { + offset = buf.handles * 4 + TPM_HEADER_SIZE; + head = (struct tpm_header *)buf.data; + if (tpm_buf_length(&buf) == offset) + head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); + } } - tpm_buf_fill_hmac_session(chip, &buf); + rc = tpm_buf_fill_hmac_session(chip, &buf); + if (rc) + goto out; + rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); rc = tpm_buf_check_hmac_response(chip, &buf, rc); - if (rc > 0) - rc = -EPERM; if (!rc) { data_len = be16_to_cpup( @@ -568,7 +569,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, out: tpm_buf_destroy(&buf); - return rc; + return tpm_ret_to_err(rc); } /** @@ -600,6 +601,5 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, out: tpm_put_ops(chip); - - return rc; + return tpm_ret_to_err(rc); } diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 749e2a4dcb13..6f88b507f927 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -61,10 +61,10 @@ int user_preparse(struct key_preparsed_payload *prep) struct user_key_payload *upayload; size_t datalen = prep->datalen; - if (datalen <= 0 || datalen > 32767 || !prep->data) + if (datalen == 0 || datalen > 32767 || !prep->data) return -EINVAL; - upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); + upayload = kmalloc_flex(*upayload, data, datalen); if (!upayload) return -ENOMEM; |
