diff options
Diffstat (limited to 'security')
27 files changed, 643 insertions, 212 deletions
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index cef8c466af80..6239777090c4 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -2064,7 +2064,6 @@ static struct ctl_table apparmor_sysctl_table[] = { .mode = 0600, .proc_handler = apparmor_dointvec, }, - { } }; static int __init apparmor_init_sysctl(void) diff --git a/security/keys/gc.c b/security/keys/gc.c index eaddaceda14e..7d687b0962b1 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -155,14 +155,6 @@ static noinline void key_gc_unused_keys(struct list_head *keys) security_key_free(key); - /* deal with the user's key tracking and quota */ - if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { - spin_lock(&key->user->lock); - key->user->qnkeys--; - key->user->qnbytes -= key->quotalen; - spin_unlock(&key->user->lock); - } - atomic_dec(&key->user->nkeys); if (state != KEY_IS_UNINSTANTIATED) atomic_dec(&key->user->nikeys); diff --git a/security/keys/key.c b/security/keys/key.c index 560790038329..3d7d185019d3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -230,6 +230,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, struct key *key; size_t desclen, quotalen; int ret; + unsigned long irqflags; key = ERR_PTR(-EINVAL); if (!desc || !*desc) @@ -259,7 +260,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ? key_quota_root_maxbytes : key_quota_maxbytes; - spin_lock(&user->lock); + spin_lock_irqsave(&user->lock, irqflags); if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) { if (user->qnkeys + 1 > maxkeys || user->qnbytes + quotalen > maxbytes || @@ -269,7 +270,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, user->qnkeys++; user->qnbytes += quotalen; - spin_unlock(&user->lock); + spin_unlock_irqrestore(&user->lock, irqflags); } /* allocate and initialise the key and its description */ @@ -327,10 +328,10 @@ security_error: kfree(key->description); kmem_cache_free(key_jar, key); if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { - spin_lock(&user->lock); + spin_lock_irqsave(&user->lock, irqflags); user->qnkeys--; user->qnbytes -= quotalen; - spin_unlock(&user->lock); + spin_unlock_irqrestore(&user->lock, irqflags); } key_user_put(user); key = ERR_PTR(ret); @@ -340,10 +341,10 @@ no_memory_3: kmem_cache_free(key_jar, key); no_memory_2: if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { - spin_lock(&user->lock); + spin_lock_irqsave(&user->lock, irqflags); user->qnkeys--; user->qnbytes -= quotalen; - spin_unlock(&user->lock); + spin_unlock_irqrestore(&user->lock, irqflags); } key_user_put(user); no_memory_1: @@ -351,7 +352,7 @@ no_memory_1: goto error; no_quota: - spin_unlock(&user->lock); + spin_unlock_irqrestore(&user->lock, irqflags); key_user_put(user); key = ERR_PTR(-EDQUOT); goto error; @@ -380,8 +381,9 @@ int key_payload_reserve(struct key *key, size_t datalen) if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ? key_quota_root_maxbytes : key_quota_maxbytes; + unsigned long flags; - spin_lock(&key->user->lock); + spin_lock_irqsave(&key->user->lock, flags); if (delta > 0 && (key->user->qnbytes + delta > maxbytes || @@ -392,7 +394,7 @@ int key_payload_reserve(struct key *key, size_t datalen) key->user->qnbytes += delta; key->quotalen += delta; } - spin_unlock(&key->user->lock); + spin_unlock_irqrestore(&key->user->lock, flags); } /* change the recorded data length if that didn't generate an error */ @@ -463,7 +465,8 @@ static int __key_instantiate_and_link(struct key *key, if (authkey) key_invalidate(authkey); - key_set_expiry(key, prep->expiry); + if (prep->expiry != TIME64_MAX) + key_set_expiry(key, prep->expiry); } } @@ -645,8 +648,18 @@ void key_put(struct key *key) if (key) { key_check(key); - if (refcount_dec_and_test(&key->usage)) + if (refcount_dec_and_test(&key->usage)) { + unsigned long flags; + + /* deal with the user's key tracking and quota */ + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { + spin_lock_irqsave(&key->user->lock, flags); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock_irqrestore(&key->user->lock, flags); + } schedule_work(&key_gc_work); + } } } EXPORT_SYMBOL(key_put); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 10ba439968f7..4bc3e9398ee3 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -954,6 +954,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) long ret; kuid_t uid; kgid_t gid; + unsigned long flags; uid = make_kuid(current_user_ns(), user); gid = make_kgid(current_user_ns(), group); @@ -1010,7 +1011,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ? key_quota_root_maxbytes : key_quota_maxbytes; - spin_lock(&newowner->lock); + spin_lock_irqsave(&newowner->lock, flags); if (newowner->qnkeys + 1 > maxkeys || newowner->qnbytes + key->quotalen > maxbytes || newowner->qnbytes + key->quotalen < @@ -1019,12 +1020,12 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group) newowner->qnkeys++; newowner->qnbytes += key->quotalen; - spin_unlock(&newowner->lock); + spin_unlock_irqrestore(&newowner->lock, flags); - spin_lock(&key->user->lock); + spin_lock_irqsave(&key->user->lock, flags); key->user->qnkeys--; key->user->qnbytes -= key->quotalen; - spin_unlock(&key->user->lock); + spin_unlock_irqrestore(&key->user->lock, flags); } atomic_dec(&key->user->nkeys); @@ -1056,7 +1057,7 @@ error: return ret; quota_overrun: - spin_unlock(&newowner->lock); + spin_unlock_irqrestore(&newowner->lock, flags); zapowner = newowner; ret = -EDQUOT; goto error_put; diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index b348e1679d5d..91f000eef3ad 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -66,7 +66,6 @@ static struct ctl_table key_sysctls[] = { .extra2 = (void *) SYSCTL_INT_MAX, }, #endif - { } }; static int __init init_security_keys_sysctls(void) diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig index dbfdd8536468..1fb8aa001995 100644 --- a/security/keys/trusted-keys/Kconfig +++ b/security/keys/trusted-keys/Kconfig @@ -1,3 +1,6 @@ +config HAVE_TRUSTED_KEYS + bool + config TRUSTED_KEYS_TPM bool "TPM-based trusted keys" depends on TCG_TPM >= TRUSTED_KEYS @@ -9,6 +12,7 @@ config TRUSTED_KEYS_TPM select ASN1_ENCODER select OID_REGISTRY select ASN1 + select HAVE_TRUSTED_KEYS help Enable use of the Trusted Platform Module (TPM) as trusted key backend. Trusted keys are random number symmetric keys, @@ -20,6 +24,7 @@ config TRUSTED_KEYS_TEE bool "TEE-based trusted keys" depends on TEE >= TRUSTED_KEYS default y + select HAVE_TRUSTED_KEYS help Enable use of the Trusted Execution Environment (TEE) as trusted key backend. @@ -29,10 +34,19 @@ 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 help Enable use of NXP's Cryptographic Accelerator and Assurance Module (CAAM) as trusted key backend. -if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM -comment "No trust source selected!" +config TRUSTED_KEYS_DCP + bool "DCP-based trusted keys" + depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS + default y + select HAVE_TRUSTED_KEYS + help + Enable use of NXP's DCP (Data Co-Processor) as 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 735aa0bc08ef..f0f3b27f688b 100644 --- a/security/keys/trusted-keys/Makefile +++ b/security/keys/trusted-keys/Makefile @@ -14,3 +14,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o + +trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index fee1ab2c734d..5113aeae5628 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -10,6 +10,7 @@ #include <keys/trusted-type.h> #include <keys/trusted_tee.h> #include <keys/trusted_caam.h> +#include <keys/trusted_dcp.h> #include <keys/trusted_tpm.h> #include <linux/capability.h> #include <linux/err.h> @@ -30,7 +31,7 @@ MODULE_PARM_DESC(rng, "Select trusted key RNG"); static char *trusted_key_source; module_param_named(source, trusted_key_source, charp, 0); -MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)"); +MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)"); static const struct trusted_key_source trusted_key_sources[] = { #if defined(CONFIG_TRUSTED_KEYS_TPM) @@ -42,6 +43,9 @@ static const struct trusted_key_source trusted_key_sources[] = { #if defined(CONFIG_TRUSTED_KEYS_CAAM) { "caam", &trusted_key_caam_ops }, #endif +#if defined(CONFIG_TRUSTED_KEYS_DCP) + { "dcp", &dcp_trusted_key_ops }, +#endif }; DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal); diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c new file mode 100644 index 000000000000..b5f81a05be36 --- /dev/null +++ b/security/keys/trusted-keys/trusted_dcp.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 sigma star gmbh + */ + +#include <crypto/aead.h> +#include <crypto/aes.h> +#include <crypto/algapi.h> +#include <crypto/gcm.h> +#include <crypto/skcipher.h> +#include <keys/trusted-type.h> +#include <linux/key-type.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/random.h> +#include <linux/scatterlist.h> +#include <soc/fsl/dcp.h> + +#define DCP_BLOB_VERSION 1 +#define DCP_BLOB_AUTHLEN 16 + +/** + * DOC: dcp blob format + * + * The Data Co-Processor (DCP) provides hardware-bound AES keys using its + * AES encryption engine only. It does not provide direct key sealing/unsealing. + * To make DCP hardware encryption keys usable as trust source, we define + * our own custom format that uses a hardware-bound key to secure the sealing + * key stored in the key blob. + * + * Whenever a new trusted key using DCP is generated, we generate a random 128-bit + * blob encryption key (BEK) and 128-bit nonce. The BEK and nonce are used to + * encrypt the trusted key payload using AES-128-GCM. + * + * The BEK itself is encrypted using the hardware-bound key using the DCP's AES + * encryption engine with AES-128-ECB. The encrypted BEK, generated nonce, + * BEK-encrypted payload and authentication tag make up the blob format together + * with a version number, payload length and authentication tag. + */ + +/** + * struct dcp_blob_fmt - DCP BLOB format. + * + * @fmt_version: Format version, currently being %1. + * @blob_key: Random AES 128 key which is used to encrypt @payload, + * @blob_key itself is encrypted with OTP or UNIQUE device key in + * AES-128-ECB mode by DCP. + * @nonce: Random nonce used for @payload encryption. + * @payload_len: Length of the plain text @payload. + * @payload: The payload itself, encrypted using AES-128-GCM and @blob_key, + * GCM auth tag of size DCP_BLOB_AUTHLEN is attached at the end of it. + * + * The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len + + * DCP_BLOB_AUTHLEN. + */ +struct dcp_blob_fmt { + __u8 fmt_version; + __u8 blob_key[AES_KEYSIZE_128]; + __u8 nonce[AES_KEYSIZE_128]; + __le32 payload_len; + __u8 payload[]; +} __packed; + +static bool use_otp_key; +module_param_named(dcp_use_otp_key, use_otp_key, bool, 0); +MODULE_PARM_DESC(dcp_use_otp_key, "Use OTP instead of UNIQUE key for sealing"); + +static bool skip_zk_test; +module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0); +MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed"); + +static unsigned int calc_blob_len(unsigned int payload_len) +{ + return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN; +} + +static int do_dcp_crypto(u8 *in, u8 *out, bool do_encrypt) +{ + struct skcipher_request *req = NULL; + struct scatterlist src_sg, dst_sg; + struct crypto_skcipher *tfm; + u8 paes_key[DCP_PAES_KEYSIZE]; + DECLARE_CRYPTO_WAIT(wait); + int res = 0; + + if (use_otp_key) + paes_key[0] = DCP_PAES_KEY_OTP; + else + paes_key[0] = DCP_PAES_KEY_UNIQUE; + + tfm = crypto_alloc_skcipher("ecb-paes-dcp", CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); + if (IS_ERR(tfm)) { + res = PTR_ERR(tfm); + tfm = NULL; + goto out; + } + + req = skcipher_request_alloc(tfm, GFP_NOFS); + if (!req) { + res = -ENOMEM; + goto out; + } + + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &wait); + res = crypto_skcipher_setkey(tfm, paes_key, sizeof(paes_key)); + if (res < 0) + goto out; + + sg_init_one(&src_sg, in, AES_KEYSIZE_128); + sg_init_one(&dst_sg, out, AES_KEYSIZE_128); + skcipher_request_set_crypt(req, &src_sg, &dst_sg, AES_KEYSIZE_128, + NULL); + + if (do_encrypt) + res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait); + else + res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait); + +out: + skcipher_request_free(req); + crypto_free_skcipher(tfm); + + return res; +} + +static int do_aead_crypto(u8 *in, u8 *out, size_t len, u8 *key, u8 *nonce, + bool do_encrypt) +{ + struct aead_request *aead_req = NULL; + struct scatterlist src_sg, dst_sg; + struct crypto_aead *aead; + int ret; + + aead = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(aead)) { + ret = PTR_ERR(aead); + goto out; + } + + ret = crypto_aead_setauthsize(aead, DCP_BLOB_AUTHLEN); + if (ret < 0) { + pr_err("Can't set crypto auth tag len: %d\n", ret); + goto free_aead; + } + + aead_req = aead_request_alloc(aead, GFP_KERNEL); + if (!aead_req) { + ret = -ENOMEM; + goto free_aead; + } + + sg_init_one(&src_sg, in, len); + if (do_encrypt) { + /* + * If we encrypt our buffer has extra space for the auth tag. + */ + sg_init_one(&dst_sg, out, len + DCP_BLOB_AUTHLEN); + } else { + sg_init_one(&dst_sg, out, len); + } + + aead_request_set_crypt(aead_req, &src_sg, &dst_sg, len, nonce); + aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, + NULL); + aead_request_set_ad(aead_req, 0); + + if (crypto_aead_setkey(aead, key, AES_KEYSIZE_128)) { + pr_err("Can't set crypto AEAD key\n"); + ret = -EINVAL; + goto free_req; + } + + if (do_encrypt) + ret = crypto_aead_encrypt(aead_req); + else + ret = crypto_aead_decrypt(aead_req); + +free_req: + aead_request_free(aead_req); +free_aead: + crypto_free_aead(aead); +out: + return ret; +} + +static int decrypt_blob_key(u8 *key) +{ + return do_dcp_crypto(key, key, false); +} + +static int encrypt_blob_key(u8 *key) +{ + return do_dcp_crypto(key, key, true); +} + +static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob) +{ + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; + int blen, ret; + + blen = calc_blob_len(p->key_len); + if (blen > MAX_BLOB_SIZE) + return -E2BIG; + + b->fmt_version = DCP_BLOB_VERSION; + get_random_bytes(b->nonce, AES_KEYSIZE_128); + get_random_bytes(b->blob_key, AES_KEYSIZE_128); + + ret = do_aead_crypto(p->key, b->payload, p->key_len, b->blob_key, + b->nonce, true); + if (ret) { + pr_err("Unable to encrypt blob payload: %i\n", ret); + return ret; + } + + ret = encrypt_blob_key(b->blob_key); + if (ret) { + pr_err("Unable to encrypt blob key: %i\n", ret); + return ret; + } + + b->payload_len = get_unaligned_le32(&p->key_len); + p->blob_len = blen; + return 0; +} + +static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob) +{ + struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob; + int blen, ret; + + if (b->fmt_version != DCP_BLOB_VERSION) { + pr_err("DCP blob has bad version: %i, expected %i\n", + b->fmt_version, DCP_BLOB_VERSION); + ret = -EINVAL; + goto out; + } + + p->key_len = le32_to_cpu(b->payload_len); + blen = calc_blob_len(p->key_len); + if (blen != p->blob_len) { + pr_err("DCP blob has bad length: %i != %i\n", blen, + p->blob_len); + ret = -EINVAL; + goto out; + } + + ret = decrypt_blob_key(b->blob_key); + if (ret) { + pr_err("Unable to decrypt blob key: %i\n", ret); + goto out; + } + + ret = do_aead_crypto(b->payload, p->key, p->key_len + DCP_BLOB_AUTHLEN, + b->blob_key, b->nonce, false); + if (ret) { + pr_err("Unwrap of DCP payload failed: %i\n", ret); + goto out; + } + + ret = 0; +out: + return ret; +} + +static int test_for_zero_key(void) +{ + /* + * Encrypting a plaintext of all 0x55 bytes will yield + * this ciphertext in case the DCP test key is used. + */ + static const u8 bad[] = {0x9a, 0xda, 0xe0, 0x54, 0xf6, 0x3d, 0xfa, 0xff, + 0x5e, 0xa1, 0x8e, 0x45, 0xed, 0xf6, 0xea, 0x6f}; + void *buf = NULL; + int ret = 0; + + if (skip_zk_test) + goto out; + + buf = kmalloc(AES_BLOCK_SIZE, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + memset(buf, 0x55, AES_BLOCK_SIZE); + + ret = do_dcp_crypto(buf, buf, true); + if (ret) + goto out; + + if (memcmp(buf, bad, AES_BLOCK_SIZE) == 0) { + pr_warn("Device neither in secure nor trusted mode!\n"); + ret = -EINVAL; + } +out: + kfree(buf); + return ret; +} + +static int trusted_dcp_init(void) +{ + int ret; + + if (use_otp_key) + pr_info("Using DCP OTP key\n"); + + ret = test_for_zero_key(); + if (ret) { + pr_warn("Test for zero'ed keys failed: %i\n", ret); + + return -EINVAL; + } + + return register_key_type(&key_type_trusted); +} + +static void trusted_dcp_exit(void) +{ + unregister_key_type(&key_type_trusted); +} + +struct trusted_key_ops dcp_trusted_key_ops = { + .exit = trusted_dcp_exit, + .init = trusted_dcp_init, + .seal = trusted_dcp_seal, + .unseal = trusted_dcp_unseal, + .migratable = 0, +}; diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index aa108bea6739..89c9798d1800 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -356,17 +356,28 @@ out: */ int trusted_tpm_send(unsigned char *cmd, size_t buflen) { + struct tpm_buf buf; int rc; if (!chip) return -ENODEV; + rc = tpm_try_get_ops(chip); + if (rc) + return rc; + + buf.flags = 0; + buf.length = buflen; + buf.data = cmd; dump_tpm_buf(cmd); - rc = tpm_send(chip, cmd, buflen); + rc = tpm_transmit_cmd(chip, &buf, 4, "sending data"); dump_tpm_buf(cmd); + if (rc > 0) - /* Can't return positive return codes values to keyctl */ + /* TPM error */ rc = -EPERM; + + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(trusted_tpm_send); @@ -407,7 +418,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, tpm_buf_append_u32(tb, handle); tpm_buf_append(tb, ononce, TPM_NONCE_SIZE); - ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, tb->length); if (ret < 0) return ret; @@ -431,7 +442,7 @@ int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) return -ENODEV; tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP); - ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, tb->length); if (ret < 0) return ret; @@ -543,7 +554,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, tpm_buf_append_u8(tb, cont); tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, tb->length); if (ret < 0) goto out; @@ -634,7 +645,7 @@ static int tpm_unseal(struct tpm_buf *tb, tpm_buf_append_u8(tb, cont); tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE); - ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); + ret = trusted_tpm_send(tb->data, tb->length); if (ret < 0) { pr_info("authhmac failed (%d)\n", ret); return ret; diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index bc700f85f80b..dfeec06301ce 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -228,8 +228,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options) { + off_t offset = TPM_HEADER_SIZE; + struct tpm_buf buf, sized; int blob_len = 0; - struct tpm_buf buf; u32 hash; u32 flags; int i; @@ -252,50 +253,58 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (rc) return rc; + rc = tpm2_start_auth_session(chip); + if (rc) + goto out_put; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); if (rc) { - tpm_put_ops(chip); - return rc; + tpm2_end_auth_session(chip); + goto out_put; } - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); + rc = tpm_buf_init_sized(&sized); + if (rc) { + tpm_buf_destroy(&buf); + tpm2_end_auth_session(chip); + goto out_put; + } + + tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT, + options->keyauth, TPM_DIGEST_SIZE); /* sensitive */ - tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len); + tpm_buf_append_u16(&sized, options->blobauth_len); - tpm_buf_append_u16(&buf, options->blobauth_len); if (options->blobauth_len) - tpm_buf_append(&buf, options->blobauth, options->blobauth_len); + tpm_buf_append(&sized, options->blobauth, options->blobauth_len); - tpm_buf_append_u16(&buf, payload->key_len); - tpm_buf_append(&buf, payload->key, payload->key_len); + tpm_buf_append_u16(&sized, payload->key_len); + tpm_buf_append(&sized, payload->key, payload->key_len); + tpm_buf_append(&buf, sized.data, sized.length); /* public */ - tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, hash); + tpm_buf_reset_sized(&sized); + tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH); + tpm_buf_append_u16(&sized, hash); /* key properties */ flags = 0; flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH; - flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | - TPM2_OA_FIXED_PARENT); - tpm_buf_append_u32(&buf, flags); + flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT); + tpm_buf_append_u32(&sized, flags); /* policy */ - tpm_buf_append_u16(&buf, options->policydigest_len); + tpm_buf_append_u16(&sized, options->policydigest_len); if (options->policydigest_len) - tpm_buf_append(&buf, options->policydigest, - options->policydigest_len); + tpm_buf_append(&sized, options->policydigest, options->policydigest_len); /* public parameters */ - tpm_buf_append_u16(&buf, TPM_ALG_NULL); - tpm_buf_append_u16(&buf, 0); + tpm_buf_append_u16(&sized, TPM_ALG_NULL); + tpm_buf_append_u16(&sized, 0); + + tpm_buf_append(&buf, sized.data, sized.length); /* outside info */ tpm_buf_append_u16(&buf, 0); @@ -305,28 +314,30 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; + tpm2_end_auth_session(chip); goto out; } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (rc) goto out; - blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); - if (blob_len > MAX_BLOB_SIZE) { + blob_len = tpm_buf_read_u32(&buf, &offset); + if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) { rc = -E2BIG; goto out; } - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { + if (buf.length - offset < blob_len) { rc = -EFAULT; goto out; } - blob_len = tpm2_key_encode(payload, options, - &buf.data[TPM_HEADER_SIZE + 4], - blob_len); + blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len); out: + tpm_buf_destroy(&sized); tpm_buf_destroy(&buf); if (rc > 0) { @@ -340,6 +351,7 @@ out: else payload->blob_len = blob_len; +out_put: tpm_put_ops(chip); return rc; } @@ -409,25 +421,31 @@ static int tpm2_load_cmd(struct tpm_chip *chip, if (blob_len > payload->blob_len) return -E2BIG; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + rc = tpm2_start_auth_session(chip); if (rc) return rc; - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (rc) { + tpm2_end_auth_session(chip); + return rc; + } + + tpm_buf_append_name(chip, &buf, options->keyhandle, NULL); + tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth, + TPM_DIGEST_SIZE); tpm_buf_append(&buf, blob, blob_len); if (buf.flags & TPM_BUF_OVERFLOW) { rc = -E2BIG; + tpm2_end_auth_session(chip); goto out; } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -465,20 +483,44 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, u8 *data; int rc; - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + rc = tpm2_start_auth_session(chip); if (rc) return rc; - tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, - options->policyhandle ? - options->policyhandle : TPM2_RS_PW, - NULL /* nonce */, 0, - TPM2_SA_CONTINUE_SESSION, - options->blobauth /* hmac */, - options->blobauth_len); + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + if (rc) { + tpm2_end_auth_session(chip); + return rc; + } + + tpm_buf_append_name(chip, &buf, blob_handle, NULL); + + if (!options->policyhandle) { + tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, + options->blobauth, + options->blobauth_len); + } else { + /* + * FIXME: The policy session was generated outside the + * kernel so we don't known the nonce and thus can't + * calculate a HMAC on it. Therefore, the user can + * only really use TPM2_PolicyPassword and we must + * send down the plain text password, which could be + * intercepted. We can still encrypt the returned + * key, but that's small comfort since the interposer + * could repeat our actions with the exfiltrated + * password. + */ + 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); + } + tpm_buf_fill_hmac_session(chip, &buf); rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); + rc = tpm_buf_check_hmac_response(chip, &buf, rc); if (rc > 0) rc = -EPERM; diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 8e93cda130f1..93fd4d47b334 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -63,7 +63,6 @@ static struct ctl_table loadpin_sysctl_table[] = { .extra1 = SYSCTL_ONE, .extra2 = SYSCTL_ONE, }, - { } }; static void set_sysctl(bool is_writable) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9f018f51e47a..7eed331e90f0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2961,7 +2961,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode) { - const struct task_security_struct *tsec = selinux_cred(current_cred()); + u32 sid = current_sid(); struct common_audit_data ad; struct inode_security_struct *isec; int rc; @@ -2990,7 +2990,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, } else { isec->sclass = SECCLASS_ANON_INODE; rc = security_transition_sid( - tsec->sid, tsec->sid, + sid, sid, isec->sclass, name, &isec->sid); if (rc) return rc; @@ -3005,7 +3005,7 @@ static int selinux_inode_init_security_anon(struct inode *inode, ad.type = LSM_AUDIT_DATA_ANONINODE; ad.u.anonclass = name ? (const char *)name->name : "?"; - return avc_has_perm(tsec->sid, + return avc_has_perm(sid, isec->sid, isec->sclass, FILE__CREATE, @@ -3063,14 +3063,12 @@ static int selinux_inode_readlink(struct dentry *dentry) static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) { - const struct cred *cred = current_cred(); struct common_audit_data ad; struct inode_security_struct *isec; - u32 sid; + u32 sid = current_sid(); ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - sid = cred_sid(cred); isec = inode_security_rcu(inode, rcu); if (IS_ERR(isec)) return PTR_ERR(isec); @@ -3094,12 +3092,11 @@ static noinline int audit_inode_permission(struct inode *inode, static int selinux_inode_permission(struct inode *inode, int mask) { - const struct cred *cred = current_cred(); u32 perms; bool from_access; bool no_block = mask & MAY_NOT_BLOCK; struct inode_security_struct *isec; - u32 sid; + u32 sid = current_sid(); struct av_decision avd; int rc, rc2; u32 audited, denied; @@ -3116,7 +3113,6 @@ static int selinux_inode_permission(struct inode *inode, int mask) perms = file_mask_to_av(inode->i_mode, mask); - sid = cred_sid(cred); isec = inode_security_rcu(inode, no_block); if (IS_ERR(isec)) return PTR_ERR(isec); @@ -5564,13 +5560,7 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) static int selinux_secmark_relabel_packet(u32 sid) { - const struct task_security_struct *tsec; - u32 tsid; - - tsec = selinux_cred(current_cred()); - tsid = tsec->sid; - - return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, + return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO, NULL); } @@ -6348,55 +6338,55 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) static int selinux_lsm_getattr(unsigned int attr, struct task_struct *p, char **value) { - const struct task_security_struct *__tsec; - u32 sid; + const struct task_security_struct *tsec; int error; - unsigned len; + u32 sid; + u32 len; rcu_read_lock(); - __tsec = selinux_cred(__task_cred(p)); - - if (current != p) { - error = avc_has_perm(current_sid(), __tsec->sid, + tsec = selinux_cred(__task_cred(p)); + if (p != current) { + error = avc_has_perm(current_sid(), tsec->sid, SECCLASS_PROCESS, PROCESS__GETATTR, NULL); if (error) - goto bad; + goto err_unlock; } - switch (attr) { case LSM_ATTR_CURRENT: - sid = __tsec->sid; + sid = tsec->sid; break; case LSM_ATTR_PREV: - sid = __tsec->osid; + sid = tsec->osid; break; case LSM_ATTR_EXEC: - sid = __tsec->exec_sid; + sid = tsec->exec_sid; break; case LSM_ATTR_FSCREATE: - sid = __tsec->create_sid; + sid = tsec->create_sid; break; case LSM_ATTR_KEYCREATE: - sid = __tsec->keycreate_sid; + sid = tsec->keycreate_sid; break; case LSM_ATTR_SOCKCREATE: - sid = __tsec->sockcreate_sid; + sid = tsec->sockcreate_sid; break; default: error = -EOPNOTSUPP; - goto bad; + goto err_unlock; } rcu_read_unlock(); - if (!sid) + if (sid == SECSID_NULL) { + *value = NULL; return 0; + } error = security_sid_to_context(sid, value, &len); if (error) return error; return len; -bad: +err_unlock: rcu_read_unlock(); return error; } diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 8f182800e412..55885634e880 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -402,7 +402,10 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) secattr = selinux_netlbl_sock_genattr(sk); if (secattr == NULL) return -ENOMEM; - rc = netlbl_sock_setattr(sk, family, secattr); + /* On socket creation, replacement of IP options is safe even if + * the caller does not hold the socket lock. + */ + rc = netlbl_sock_setattr(sk, family, secattr, true); switch (rc) { case 0: sksec->nlbl_state = NLBL_LABELED; diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 074d6c2714eb..e172f182b65c 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -571,11 +571,18 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; + struct selinux_fs_info *fsi; struct selinux_load_state load_state; ssize_t length; void *data = NULL; + /* no partial writes */ + if (*ppos) + return -EINVAL; + /* no empty policies */ + if (!count) + return -EINVAL; + mutex_lock(&selinux_state.policy_mutex); length = avc_has_perm(current_sid(), SECINITSID_SECURITY, @@ -583,26 +590,22 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, if (length) goto out; - /* No partial writes. */ - length = -EINVAL; - if (*ppos != 0) - goto out; - - length = -ENOMEM; data = vmalloc(count); - if (!data) + if (!data) { + length = -ENOMEM; goto out; - - length = -EFAULT; - if (copy_from_user(data, buf, count) != 0) + } + if (copy_from_user(data, buf, count) != 0) { + length = -EFAULT; goto out; + } length = security_load_policy(data, count, &load_state); if (length) { pr_warn_ratelimited("SELinux: failed to load policy\n"); goto out; } - + fsi = file_inode(file)->i_sb->s_fs_info; length = sel_make_policy_nodes(fsi, load_state.policy); if (length) { pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n"); @@ -611,13 +614,12 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf, } selinux_policy_commit(&load_state); - length = count; - audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, "auid=%u ses=%u lsm=selinux res=1", from_kuid(&init_user_ns, audit_get_loginuid(current)), audit_get_sessionid(current)); + out: mutex_unlock(&selinux_state.policy_mutex); vfree(data); @@ -2161,6 +2163,12 @@ static int __init init_sel_fs(void) return err; } + /* + * Try to pre-allocate the status page, so the sequence number of the + * initial policy load can be stored. + */ + (void) selinux_kernel_status_page(); + return err; } diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index f12476855b27..64ba95e40a6f 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -169,6 +169,9 @@ int cond_init_bool_indexes(struct policydb *p) p->p_bools.nprim, sizeof(*p->bool_val_to_struct), GFP_KERNEL); if (!p->bool_val_to_struct) return -ENOMEM; + + avtab_hash_eval(&p->te_cond_avtab, "conditional_rules"); + return 0; } @@ -600,7 +603,8 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, } } -static int cond_dup_av_list(struct cond_av_list *new, struct cond_av_list *orig, +static int cond_dup_av_list(struct cond_av_list *new, + const struct cond_av_list *orig, struct avtab *avtab) { u32 i; @@ -623,7 +627,7 @@ static int cond_dup_av_list(struct cond_av_list *new, struct cond_av_list *orig, } static int duplicate_policydb_cond_list(struct policydb *newp, - struct policydb *origp) + const struct policydb *origp) { int rc; u32 i; @@ -640,7 +644,7 @@ static int duplicate_policydb_cond_list(struct policydb *newp, for (i = 0; i < origp->cond_list_len; i++) { struct cond_node *newn = &newp->cond_list[i]; - struct cond_node *orign = &origp->cond_list[i]; + const struct cond_node *orign = &origp->cond_list[i]; newp->cond_list_len++; @@ -680,8 +684,8 @@ static int cond_bools_destroy(void *key, void *datum, void *args) return 0; } -static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, - void *args) +static int cond_bools_copy(struct hashtab_node *new, + const struct hashtab_node *orig, void *args) { struct cond_bool_datum *datum; @@ -707,7 +711,7 @@ static int cond_bools_index(void *key, void *datum, void *args) } static int duplicate_policydb_bools(struct policydb *newdb, - struct policydb *orig) + const struct policydb *orig) { struct cond_bool_datum **cond_bool_array; int rc; @@ -740,7 +744,7 @@ void cond_policydb_destroy_dup(struct policydb *p) cond_policydb_destroy(p); } -int cond_policydb_dup(struct policydb *new, struct policydb *orig) +int cond_policydb_dup(struct policydb *new, const struct policydb *orig) { cond_policydb_init(new); diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index b972ce40db18..8827715bad75 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -79,6 +79,6 @@ void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key, struct extended_perms_decision *xpermd); void evaluate_cond_nodes(struct policydb *p); void cond_policydb_destroy_dup(struct policydb *p); -int cond_policydb_dup(struct policydb *new, struct policydb *orig); +int cond_policydb_dup(struct policydb *new, const struct policydb *orig); #endif /* _CONDITIONAL_H_ */ diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 67c1a73cd5ee..04d7f4907a06 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -21,7 +21,7 @@ #include "ebitmap.h" #include "policydb.h" -#define BITS_PER_U64 (sizeof(u64) * 8) +#define BITS_PER_U64 ((u32)(sizeof(u64) * 8)) static struct kmem_cache *ebitmap_node_cachep __ro_after_init; @@ -79,7 +79,8 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2) { struct ebitmap_node *n; - int bit, rc; + u32 bit; + int rc; ebitmap_init(dst); @@ -256,7 +257,7 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, return 1; } -int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit) +int ebitmap_get_bit(const struct ebitmap *e, u32 bit) { const struct ebitmap_node *n; @@ -273,7 +274,7 @@ int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit) return 0; } -int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) +int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value) { struct ebitmap_node *n, *prev, *new; @@ -284,7 +285,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) if (value) { ebitmap_node_set_bit(n, bit); } else { - unsigned int s; + u32 s; ebitmap_node_clr_bit(n, bit); @@ -362,12 +363,12 @@ void ebitmap_destroy(struct ebitmap *e) int ebitmap_read(struct ebitmap *e, void *fp) { struct ebitmap_node *n = NULL; - u32 mapunit, count, startbit, index; + u32 mapunit, count, startbit, index, i; __le32 ebitmap_start; u64 map; __le64 mapbits; __le32 buf[3]; - int rc, i; + int rc; ebitmap_init(e); @@ -381,7 +382,7 @@ int ebitmap_read(struct ebitmap *e, void *fp) if (mapunit != BITS_PER_U64) { pr_err("SELinux: ebitmap: map size %u does not " - "match my size %zd (high bit was %d)\n", + "match my size %u (high bit was %u)\n", mapunit, BITS_PER_U64, e->highbit); goto bad; } @@ -407,13 +408,13 @@ int ebitmap_read(struct ebitmap *e, void *fp) startbit = le32_to_cpu(ebitmap_start); if (startbit & (mapunit - 1)) { - pr_err("SELinux: ebitmap start bit (%d) is " + pr_err("SELinux: ebitmap start bit (%u) is " "not a multiple of the map unit size (%u)\n", startbit, mapunit); goto bad; } if (startbit > e->highbit - mapunit) { - pr_err("SELinux: ebitmap start bit (%d) is " + pr_err("SELinux: ebitmap start bit (%u) is " "beyond the end of the bitmap (%u)\n", startbit, (e->highbit - mapunit)); goto bad; @@ -436,8 +437,8 @@ int ebitmap_read(struct ebitmap *e, void *fp) e->node = tmp; n = tmp; } else if (startbit <= n->startbit) { - pr_err("SELinux: ebitmap: start bit %d" - " comes after start bit %d\n", + pr_err("SELinux: ebitmap: start bit %u" + " comes after start bit %u\n", startbit, n->startbit); goto bad; } @@ -448,6 +449,10 @@ int ebitmap_read(struct ebitmap *e, void *fp) goto bad; } map = le64_to_cpu(mapbits); + if (!map) { + pr_err("SELinux: ebitmap: empty map\n"); + goto bad; + } index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE; while (map) { @@ -455,6 +460,13 @@ int ebitmap_read(struct ebitmap *e, void *fp) map = EBITMAP_SHIFT_UNIT_SIZE(map); } } + + if (n && n->startbit + EBITMAP_SIZE != e->highbit) { + pr_err("SELinux: ebitmap: high bit %u is not equal to the expected value %zu\n", + e->highbit, n->startbit + EBITMAP_SIZE); + goto bad; + } + ok: rc = 0; out: @@ -469,19 +481,20 @@ bad: int ebitmap_write(const struct ebitmap *e, void *fp) { struct ebitmap_node *n; - u32 count; + u32 bit, count, last_bit, last_startbit; __le32 buf[3]; u64 map; - int bit, last_bit, last_startbit, rc; + int rc; buf[0] = cpu_to_le32(BITS_PER_U64); count = 0; last_bit = 0; - last_startbit = -1; + last_startbit = U32_MAX; ebitmap_for_each_positive_bit(e, n, bit) { - if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + if (last_startbit == U32_MAX || + rounddown(bit, BITS_PER_U64) > last_startbit) { count++; last_startbit = rounddown(bit, BITS_PER_U64); } @@ -495,10 +508,11 @@ int ebitmap_write(const struct ebitmap *e, void *fp) return rc; map = 0; - last_startbit = INT_MIN; + last_startbit = U32_MAX; ebitmap_for_each_positive_bit(e, n, bit) { - if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) { + if (last_startbit == U32_MAX || + rounddown(bit, BITS_PER_U64) > last_startbit) { __le64 buf64[1]; /* this is the very first bit */ diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 02798b35eecc..24d7d8b3cda3 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -46,10 +46,10 @@ struct ebitmap { #define ebitmap_length(e) ((e)->highbit) -static inline unsigned int ebitmap_start_positive(const struct ebitmap *e, - struct ebitmap_node **n) +static inline u32 ebitmap_start_positive(const struct ebitmap *e, + struct ebitmap_node **n) { - unsigned int ofs; + u32 ofs; for (*n = e->node; *n; *n = (*n)->next) { ofs = find_first_bit((*n)->maps, EBITMAP_SIZE); @@ -64,11 +64,10 @@ static inline void ebitmap_init(struct ebitmap *e) memset(e, 0, sizeof(*e)); } -static inline unsigned int ebitmap_next_positive(const struct ebitmap *e, - struct ebitmap_node **n, - unsigned int bit) +static inline u32 ebitmap_next_positive(const struct ebitmap *e, + struct ebitmap_node **n, u32 bit) { - unsigned int ofs; + u32 ofs; ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1); if (ofs < EBITMAP_SIZE) @@ -87,11 +86,10 @@ static inline unsigned int ebitmap_next_positive(const struct ebitmap *e, #define EBITMAP_NODE_OFFSET(node, bit) \ (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE) -static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, - unsigned int bit) +static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); if ((n->maps[index] & (EBITMAP_BIT << ofs))) @@ -99,21 +97,19 @@ static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, return 0; } -static inline void ebitmap_node_set_bit(struct ebitmap_node *n, - unsigned int bit) +static inline void ebitmap_node_set_bit(struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); n->maps[index] |= (EBITMAP_BIT << ofs); } -static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, - unsigned int bit) +static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, u32 bit) { - unsigned int index = EBITMAP_NODE_INDEX(n, bit); - unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit); + u32 index = EBITMAP_NODE_INDEX(n, bit); + u32 ofs = EBITMAP_NODE_OFFSET(n, bit); BUG_ON(index >= EBITMAP_UNIT_NUMS); n->maps[index] &= ~(EBITMAP_BIT << ofs); @@ -130,8 +126,8 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2); int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit); -int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit); -int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); +int ebitmap_get_bit(const struct ebitmap *e, u32 bit); +int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value); void ebitmap_destroy(struct ebitmap *e); int ebitmap_read(struct ebitmap *e, void *fp); int ebitmap_write(const struct ebitmap *e, void *fp); diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 754bedbde133..32c4cb37f3d2 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -136,11 +136,12 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info) } #endif /* CONFIG_SECURITY_SELINUX_DEBUG */ -int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, +int hashtab_duplicate(struct hashtab *new, const struct hashtab *orig, int (*copy)(struct hashtab_node *new, - struct hashtab_node *orig, void *args), + const struct hashtab_node *orig, void *args), int (*destroy)(void *k, void *d, void *args), void *args) { + const struct hashtab_node *orig_cur; struct hashtab_node *cur, *tmp, *tail; u32 i; int rc; @@ -155,12 +156,13 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, for (i = 0; i < orig->size; i++) { tail = NULL; - for (cur = orig->htable[i]; cur; cur = cur->next) { + for (orig_cur = orig->htable[i]; orig_cur; + orig_cur = orig_cur->next) { tmp = kmem_cache_zalloc(hashtab_node_cachep, GFP_KERNEL); if (!tmp) goto error; - rc = copy(tmp, cur, args); + rc = copy(tmp, orig_cur, args); if (rc) { kmem_cache_free(hashtab_node_cachep, tmp); goto error; diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h index 5f74dcc1360f..deba82d78c3a 100644 --- a/security/selinux/ss/hashtab.h +++ b/security/selinux/ss/hashtab.h @@ -136,9 +136,9 @@ void hashtab_destroy(struct hashtab *h); int hashtab_map(struct hashtab *h, int (*apply)(void *k, void *d, void *args), void *args); -int hashtab_duplicate(struct hashtab *new, struct hashtab *orig, +int hashtab_duplicate(struct hashtab *new, const struct hashtab *orig, int (*copy)(struct hashtab_node *new, - struct hashtab_node *orig, void *args), + const struct hashtab_node *orig, void *args), int (*destroy)(void *k, void *d, void *args), void *args); #ifdef CONFIG_SECURITY_SELINUX_DEBUG diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 3d22d5baa829..383f3ae82a73 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -672,14 +672,16 @@ static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = { /* clang-format on */ #ifdef CONFIG_SECURITY_SELINUX_DEBUG -static void hash_eval(struct hashtab *h, const char *hash_name) +static void hash_eval(struct hashtab *h, const char *hash_name, + const char *hash_details) { struct hashtab_info info; hashtab_stat(h, &info); pr_debug( - "SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n", - hash_name, h->nel, info.slots_used, h->size, info.max_chain_len, + "SELinux: %s%s%s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n", + hash_name, hash_details ? "@" : "", hash_details ?: "", h->nel, + info.slots_used, h->size, info.max_chain_len, info.chain2_len_sum); } @@ -688,11 +690,12 @@ static void symtab_hash_eval(struct symtab *s) int i; for (i = 0; i < SYM_NUM; i++) - hash_eval(&s[i].table, symtab_name[i]); + hash_eval(&s[i].table, symtab_name[i], NULL); } #else -static inline void hash_eval(struct hashtab *h, const char *hash_name) +static inline void hash_eval(struct hashtab *h, const char *hash_name, + const char *hash_details) { } static inline void symtab_hash_eval(struct symtab *s) @@ -1178,6 +1181,8 @@ static int common_read(struct policydb *p, struct symtab *s, void *fp) goto bad; } + hash_eval(&comdatum->permissions.table, "common_permissions", key); + rc = symtab_insert(s, key, comdatum); if (rc) goto bad; @@ -1358,6 +1363,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp) goto bad; } + hash_eval(&cladatum->permissions.table, "class_permissions", key); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; @@ -1898,7 +1905,7 @@ static int range_read(struct policydb *p, void *fp) rt = NULL; r = NULL; } - hash_eval(&p->range_tr, "rangetr"); + hash_eval(&p->range_tr, "rangetr", NULL); rc = 0; out: kfree(rt); @@ -1943,6 +1950,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, void *fp) if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) { /* conflicting/duplicate rules are ignored */ datum = NULL; + rc = 0; goto out; } if (likely(datum->otype == otype)) @@ -2116,7 +2124,7 @@ static int filename_trans_read(struct policydb *p, void *fp) return rc; } } - hash_eval(&p->filename_trans, "filenametr"); + hash_eval(&p->filename_trans, "filenametr", NULL); return 0; } @@ -2649,6 +2657,8 @@ int policydb_read(struct policydb *p, void *fp) rtd = NULL; } + hash_eval(&p->role_tr, "roletr", NULL); + rc = next_entry(buf, fp, sizeof(u32)); if (rc) goto bad; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index e88b1b6c4adb..f20e1968b7f7 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -633,8 +633,7 @@ static void context_struct_compute_av(struct policydb *policydb, } if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) { - if (printk_ratelimit()) - pr_warn("SELinux: Invalid class %hu\n", tclass); + pr_warn_ratelimited("SELinux: Invalid class %u\n", tclass); return; } diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index c04f8d447873..832660fd84a9 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -12,17 +12,17 @@ static unsigned int symhash(const void *key) { - const char *p, *keyp; - unsigned int size; - unsigned int val; - - val = 0; - keyp = key; - size = strlen(keyp); - for (p = keyp; (p - keyp) < size; p++) - val = (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ - (*p); - return val; + /* + * djb2a + * Public domain from cdb v0.75 + */ + unsigned int hash = 5381; + unsigned char c; + + while ((c = *(const unsigned char *)key++)) + hash = ((hash << 5) + hash) ^ c; + + return hash; } static int symcmp(const void *key1, const void *key2) diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 95fcd2d3433e..90ec4ef1b082 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -76,7 +76,6 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, gfp_t gfp) { int rc; - const struct task_security_struct *tsec = selinux_cred(current_cred()); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; @@ -103,7 +102,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, if (rc) goto err; - rc = avc_has_perm(tsec->sid, ctx->ctx_sid, + rc = avc_has_perm(current_sid(), ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); if (rc) goto err; @@ -134,12 +133,10 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { - const struct task_security_struct *tsec = selinux_cred(current_cred()); - if (!ctx) return 0; - return avc_has_perm(tsec->sid, ctx->ctx_sid, + return avc_has_perm(current_sid(), ctx->ctx_sid, SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL); } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 79d8dc633b9a..70ba2841e181 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2565,7 +2565,8 @@ static int smack_netlbl_add(struct sock *sk) local_bh_disable(); bh_lock_sock_nested(sk); - rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel); + rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel, + netlbl_sk_lock_check(sk)); switch (rc) { case 0: ssp->smk_state = SMK_NETLBL_LABELED; diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 49dc52b454ef..b6684a074a59 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -463,7 +463,6 @@ static struct ctl_table yama_sysctl_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = &max_scope, }, - { } }; static void __init yama_init_sysctl(void) { |
