diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/Kconfig | 1 | ||||
-rw-r--r-- | security/keys/big_key.c | 15 | ||||
-rw-r--r-- | security/keys/encrypted-keys/encrypted.c | 82 | ||||
-rw-r--r-- | security/keys/key.c | 8 | ||||
-rw-r--r-- | security/keys/keyctl.c | 49 | ||||
-rw-r--r-- | security/keys/process_keys.c | 1 | ||||
-rw-r--r-- | security/keys/trusted.c | 55 |
7 files changed, 155 insertions, 56 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 72483b8f1be5..fe4d74e126a7 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -54,6 +54,7 @@ config TRUSTED_KEYS select CRYPTO select CRYPTO_HMAC select CRYPTO_SHA1 + select CRYPTO_HASH_INFO help This option provides support for creating, sealing, and unsealing keys in the kernel. Trusted keys are random number symmetric keys, diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 907c1522ee46..c721e398893a 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -9,7 +9,6 @@ * 2 of the Licence, or (at your option) any later version. */ -#include <linux/module.h> #include <linux/init.h> #include <linux/seq_file.h> #include <linux/file.h> @@ -18,8 +17,6 @@ #include <keys/user-type.h> #include <keys/big_key-type.h> -MODULE_LICENSE("GPL"); - /* * Layout of key payload words. */ @@ -212,18 +209,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) return ret; } -/* - * Module stuff - */ static int __init big_key_init(void) { return register_key_type(&key_type_big_key); } - -static void __exit big_key_cleanup(void) -{ - unregister_key_type(&key_type_big_key); -} - -module_init(big_key_init); -module_exit(big_key_cleanup); +device_initcall(big_key_init); diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 696ccfa08d10..5adbfc32242f 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -28,11 +28,10 @@ #include <linux/random.h> #include <linux/rcupdate.h> #include <linux/scatterlist.h> -#include <linux/crypto.h> #include <linux/ctype.h> #include <crypto/hash.h> #include <crypto/sha.h> -#include <crypto/aes.h> +#include <crypto/skcipher.h> #include "encrypted.h" #include "ecryptfs_format.h" @@ -85,17 +84,17 @@ static const match_table_t key_tokens = { static int aes_get_sizes(void) { - struct crypto_blkcipher *tfm; + struct crypto_skcipher *tfm; - tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to alloc_cipher (%ld)\n", PTR_ERR(tfm)); return PTR_ERR(tfm); } - ivsize = crypto_blkcipher_ivsize(tfm); - blksize = crypto_blkcipher_blocksize(tfm); - crypto_free_blkcipher(tfm); + ivsize = crypto_skcipher_ivsize(tfm); + blksize = crypto_skcipher_blocksize(tfm); + crypto_free_skcipher(tfm); return 0; } @@ -401,28 +400,37 @@ static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, return ret; } -static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, - unsigned int key_len, const u8 *iv, - unsigned int ivsize) +static struct skcipher_request *init_skcipher_req(const u8 *key, + unsigned int key_len) { + struct skcipher_request *req; + struct crypto_skcipher *tfm; int ret; - desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(desc->tfm)) { + tfm = crypto_alloc_skcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { pr_err("encrypted_key: failed to load %s transform (%ld)\n", - blkcipher_alg, PTR_ERR(desc->tfm)); - return PTR_ERR(desc->tfm); + blkcipher_alg, PTR_ERR(tfm)); + return ERR_CAST(tfm); } - desc->flags = 0; - ret = crypto_blkcipher_setkey(desc->tfm, key, key_len); + ret = crypto_skcipher_setkey(tfm, key, key_len); if (ret < 0) { pr_err("encrypted_key: failed to setkey (%d)\n", ret); - crypto_free_blkcipher(desc->tfm); - return ret; + crypto_free_skcipher(tfm); + return ERR_PTR(ret); } - crypto_blkcipher_set_iv(desc->tfm, iv, ivsize); - return 0; + + req = skcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("encrypted_key: failed to allocate request for %s\n", + blkcipher_alg); + crypto_free_skcipher(tfm); + return ERR_PTR(-ENOMEM); + } + + skcipher_request_set_callback(req, 0, NULL, NULL); + return req; } static struct key *request_master_key(struct encrypted_key_payload *epayload, @@ -467,7 +475,8 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[2]; struct scatterlist sg_out[1]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; unsigned int padlen; char pad[16]; @@ -476,9 +485,9 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); padlen = encrypted_datalen - epayload->decrypted_datalen; - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_decrypted_data(epayload); @@ -491,8 +500,12 @@ static int derived_key_encrypt(struct encrypted_key_payload *epayload, sg_init_table(sg_out, 1); sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen); - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_encrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) pr_err("encrypted_key: failed to encrypt (%d)\n", ret); else @@ -565,15 +578,16 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, { struct scatterlist sg_in[1]; struct scatterlist sg_out[2]; - struct blkcipher_desc desc; + struct crypto_skcipher *tfm; + struct skcipher_request *req; unsigned int encrypted_datalen; char pad[16]; int ret; encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); - ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, - epayload->iv, ivsize); - if (ret < 0) + req = init_skcipher_req(derived_key, derived_keylen); + ret = PTR_ERR(req); + if (IS_ERR(req)) goto out; dump_encrypted_data(epayload, encrypted_datalen); @@ -585,8 +599,12 @@ static int derived_key_decrypt(struct encrypted_key_payload *epayload, epayload->decrypted_datalen); sg_set_buf(&sg_out[1], pad, sizeof pad); - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen); - crypto_free_blkcipher(desc.tfm); + skcipher_request_set_crypt(req, sg_in, sg_out, encrypted_datalen, + epayload->iv); + ret = crypto_skcipher_decrypt(req); + tfm = crypto_skcipher_reqtfm(req); + skcipher_request_free(req); + crypto_free_skcipher(tfm); if (ret < 0) goto out; dump_decrypted_data(epayload); diff --git a/security/keys/key.c b/security/keys/key.c index ab7997ded725..b28755131687 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -296,6 +296,8 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->flags |= 1 << KEY_FLAG_IN_QUOTA; if (flags & KEY_ALLOC_TRUSTED) key->flags |= 1 << KEY_FLAG_TRUSTED; + if (flags & KEY_ALLOC_BUILT_IN) + key->flags |= 1 << KEY_FLAG_BUILTIN; #ifdef KEY_DEBUGGING key->magic = KEY_DEBUG_MAGIC; @@ -429,8 +431,12 @@ static int __key_instantiate_and_link(struct key *key, awaken = 1; /* and link it into the destination keyring */ - if (keyring) + if (keyring) { + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + set_bit(KEY_FLAG_KEEP, &key->flags); + __key_link(key, _edit); + } /* disable the authorisation key */ if (authkey) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 1c3872aeed14..ed73c6c1c326 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -358,11 +358,14 @@ error: * and any links to the key will be automatically garbage collected after a * certain amount of time (/proc/sys/kernel/keys/gc_delay). * + * Keys with KEY_FLAG_KEEP set should not be revoked. + * * If successful, 0 is returned. */ long keyctl_revoke_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE); @@ -377,8 +380,12 @@ long keyctl_revoke_key(key_serial_t id) } } - key_revoke(key_ref_to_ptr(key_ref)); + key = key_ref_to_ptr(key_ref); ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_revoke(key); key_ref_put(key_ref); error: @@ -392,11 +399,14 @@ error: * The key and any links to the key will be automatically garbage collected * immediately. * + * Keys with KEY_FLAG_KEEP set should not be invalidated. + * * If successful, 0 is returned. */ long keyctl_invalidate_key(key_serial_t id) { key_ref_t key_ref; + struct key *key; long ret; kenter("%d", id); @@ -420,8 +430,12 @@ long keyctl_invalidate_key(key_serial_t id) } invalidate: - key_invalidate(key_ref_to_ptr(key_ref)); + key = key_ref_to_ptr(key_ref); ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_invalidate(key); error_put: key_ref_put(key_ref); error: @@ -433,12 +447,13 @@ error: * Clear the specified keyring, creating an empty process keyring if one of the * special keyring IDs is used. * - * The keyring must grant the caller Write permission for this to work. If - * successful, 0 will be returned. + * The keyring must grant the caller Write permission and not have + * KEY_FLAG_KEEP set for this to work. If successful, 0 will be returned. */ long keyctl_keyring_clear(key_serial_t ringid) { key_ref_t keyring_ref; + struct key *keyring; long ret; keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); @@ -460,7 +475,11 @@ long keyctl_keyring_clear(key_serial_t ringid) } clear: - ret = keyring_clear(key_ref_to_ptr(keyring_ref)); + keyring = key_ref_to_ptr(keyring_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags)) + ret = -EPERM; + else + ret = keyring_clear(keyring); error_put: key_ref_put(keyring_ref); error: @@ -511,11 +530,14 @@ error: * itself need not grant the caller anything. If the last link to a key is * removed then that key will be scheduled for destruction. * + * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked. + * * If successful, 0 will be returned. */ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) { key_ref_t keyring_ref, key_ref; + struct key *keyring, *key; long ret; keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE); @@ -530,7 +552,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) goto error2; } - ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref)); + keyring = key_ref_to_ptr(keyring_ref); + key = key_ref_to_ptr(key_ref); + if (test_bit(KEY_FLAG_KEEP, &keyring->flags) && + test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + ret = key_unlink(keyring, key); key_ref_put(key_ref); error2: @@ -1289,6 +1317,8 @@ error: * the current time. The key and any links to the key will be automatically * garbage collected after the timeout expires. * + * Keys with KEY_FLAG_KEEP set should not be timed out. + * * If successful, 0 is returned. */ long keyctl_set_timeout(key_serial_t id, unsigned timeout) @@ -1320,10 +1350,13 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) okay: key = key_ref_to_ptr(key_ref); - key_set_timeout(key, timeout); + ret = 0; + if (test_bit(KEY_FLAG_KEEP, &key->flags)) + ret = -EPERM; + else + key_set_timeout(key, timeout); key_put(key); - ret = 0; error: return ret; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index a3f85d2a00bb..e6d50172872f 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -794,6 +794,7 @@ long join_session_keyring(const char *name) ret = PTR_ERR(keyring); goto error2; } else if (keyring == new->session_keyring) { + key_put(keyring); ret = 0; goto error2; } diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 16dec53184b6..90d61751ff12 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -11,6 +11,7 @@ * See Documentation/security/keys-trusted-encrypted.txt */ +#include <crypto/hash_info.h> #include <linux/uaccess.h> #include <linux/module.h> #include <linux/init.h> @@ -710,7 +711,10 @@ enum { Opt_err = -1, Opt_new, Opt_load, Opt_update, Opt_keyhandle, Opt_keyauth, Opt_blobauth, - Opt_pcrinfo, Opt_pcrlock, Opt_migratable + Opt_pcrinfo, Opt_pcrlock, Opt_migratable, + Opt_hash, + Opt_policydigest, + Opt_policyhandle, }; static const match_table_t key_tokens = { @@ -723,6 +727,9 @@ static const match_table_t key_tokens = { {Opt_pcrinfo, "pcrinfo=%s"}, {Opt_pcrlock, "pcrlock=%s"}, {Opt_migratable, "migratable=%s"}, + {Opt_hash, "hash=%s"}, + {Opt_policydigest, "policydigest=%s"}, + {Opt_policyhandle, "policyhandle=%s"}, {Opt_err, NULL} }; @@ -736,11 +743,23 @@ static int getoptions(char *c, struct trusted_key_payload *pay, int res; unsigned long handle; unsigned long lock; + unsigned long token_mask = 0; + unsigned int digest_len; + int i; + int tpm2; + + tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + if (tpm2 < 0) + return tpm2; + + opt->hash = tpm2 ? HASH_ALGO_SHA256 : HASH_ALGO_SHA1; 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_pcrinfo: @@ -787,6 +806,40 @@ static int getoptions(char *c, struct trusted_key_payload *pay, return -EINVAL; opt->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; + break; + } + } + if (i == HASH_ALGO__LAST) + return -EINVAL; + if (!tpm2 && i != HASH_ALGO_SHA1) { + pr_info("trusted_key: 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)) + return -EINVAL; + res = hex2bin(opt->policydigest, args[0].from, + digest_len); + if (res < 0) + return -EINVAL; + opt->policydigest_len = digest_len; + break; + case Opt_policyhandle: + if (!tpm2) + return -EINVAL; + res = kstrtoul(args[0].from, 16, &handle); + if (res < 0) + return -EINVAL; + opt->policyhandle = handle; + break; default: return -EINVAL; } |