summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorJames Morris <james.l.morris@oracle.com>2015-12-26 16:06:53 +1100
committerJames Morris <james.l.morris@oracle.com>2015-12-26 16:06:53 +1100
commit3cb92fe48138d225b8d0ceedf148642f9103f841 (patch)
treecbb877002d9d39777be13df3876e05f2542da234 /security
parent5beb0c435bdde35a09376566b0e28f7df87c9f68 (diff)
parent0112721df4edbdd07b800813300d76811572f080 (diff)
downloadop-kernel-dev-3cb92fe48138d225b8d0ceedf148642f9103f841.zip
op-kernel-dev-3cb92fe48138d225b8d0ceedf148642f9103f841.tar.gz
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity into next
Diffstat (limited to 'security')
-rw-r--r--security/integrity/Kconfig11
-rw-r--r--security/integrity/digsig.c14
-rw-r--r--security/integrity/digsig_asymmetric.c14
-rw-r--r--security/integrity/evm/Kconfig17
-rw-r--r--security/integrity/evm/evm.h3
-rw-r--r--security/integrity/evm/evm_crypto.c54
-rw-r--r--security/integrity/evm/evm_main.c32
-rw-r--r--security/integrity/evm/evm_secfs.c12
-rw-r--r--security/integrity/iint.c1
-rw-r--r--security/integrity/ima/Kconfig44
-rw-r--r--security/integrity/ima/Makefile1
-rw-r--r--security/integrity/ima/ima.h24
-rw-r--r--security/integrity/ima/ima_fs.c47
-rw-r--r--security/integrity/ima/ima_init.c2
-rw-r--r--security/integrity/ima/ima_mok.c55
-rw-r--r--security/integrity/ima/ima_policy.c301
-rw-r--r--security/integrity/integrity.h13
-rw-r--r--security/keys/key.c6
-rw-r--r--security/keys/keyctl.c56
19 files changed, 623 insertions, 84 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 73c457b..21d7568 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -41,6 +41,17 @@ config INTEGRITY_ASYMMETRIC_KEYS
This option enables digital signature verification using
asymmetric keys.
+config INTEGRITY_TRUSTED_KEYRING
+ bool "Require all keys on the integrity keyrings be signed"
+ depends on SYSTEM_TRUSTED_KEYRING
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ select KEYS_DEBUG_PROC_KEYS
+ default y
+ help
+ This option requires that all keys added to the .ima and
+ .evm keyrings be signed by a key on the system trusted
+ keyring.
+
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on AUDIT
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 5be9ffb..8ef1511 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -24,15 +24,22 @@
static struct key *keyring[INTEGRITY_KEYRING_MAX];
static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
+#ifndef CONFIG_INTEGRITY_TRUSTED_KEYRING
"_evm",
- "_module",
-#ifndef CONFIG_IMA_TRUSTED_KEYRING
"_ima",
#else
+ ".evm",
".ima",
#endif
+ "_module",
};
+#ifdef CONFIG_INTEGRITY_TRUSTED_KEYRING
+static bool init_keyring __initdata = true;
+#else
+static bool init_keyring __initdata;
+#endif
+
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen)
{
@@ -68,6 +75,9 @@ int __init integrity_init_keyring(const unsigned int id)
const struct cred *cred = current_cred();
int err = 0;
+ if (!init_keyring)
+ return 0;
+
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
KGIDT_INIT(0), cred,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 4fec181..5ade2a7 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -17,6 +17,7 @@
#include <linux/key-type.h>
#include <crypto/public_key.h>
#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
#include "integrity.h"
@@ -32,9 +33,22 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
pr_debug("key search: \"%s\"\n", name);
+ key = get_ima_blacklist_keyring();
+ if (key) {
+ key_ref_t kref;
+
+ kref = keyring_search(make_key_ref(key, 1),
+ &key_type_asymmetric, name);
+ if (!IS_ERR(kref)) {
+ pr_err("Key '%s' is in ima_blacklist_keyring\n", name);
+ return ERR_PTR(-EKEYREJECTED);
+ }
+ }
+
if (keyring) {
/* search in specific keyring */
key_ref_t kref;
+
kref = keyring_search(make_key_ref(keyring, 1),
&key_type_asymmetric, name);
if (IS_ERR(kref))
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index bf19723..e825e0a 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -42,3 +42,20 @@ config EVM_EXTRA_SMACK_XATTRS
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.
+config EVM_LOAD_X509
+ bool "Load an X509 certificate onto the '.evm' trusted keyring"
+ depends on EVM && INTEGRITY_TRUSTED_KEYRING
+ default n
+ help
+ Load an X509 certificate onto the '.evm' trusted keyring.
+
+ This option enables X509 certificate loading from the kernel
+ onto the '.evm' trusted keyring. A public key can be used to
+ verify EVM integrity starting from the 'init' process.
+
+config EVM_X509_PATH
+ string "EVM X509 certificate path"
+ depends on EVM_LOAD_X509
+ default "/etc/keys/x509_evm.der"
+ help
+ This option defines X509 certificate path.
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 88bfe77..f5f1272 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -21,6 +21,9 @@
#include "../integrity.h"
+#define EVM_INIT_HMAC 0x0001
+#define EVM_INIT_X509 0x0002
+
extern int evm_initialized;
extern char *evm_hmac;
extern char *evm_hash;
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 461f8d8..30b6b7d0 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
+#include <linux/evm.h>
#include <keys/encrypted-type.h>
#include <crypto/hash.h>
#include "evm.h"
@@ -32,6 +33,44 @@ struct crypto_shash *hash_tfm;
static DEFINE_MUTEX(mutex);
+#define EVM_SET_KEY_BUSY 0
+
+static unsigned long evm_set_key_flags;
+
+/**
+ * evm_set_key() - set EVM HMAC key from the kernel
+ * @key: pointer to a buffer with the key data
+ * @size: length of the key data
+ *
+ * This function allows setting the EVM HMAC key from the kernel
+ * without using the "encrypted" key subsystem keys. It can be used
+ * by the crypto HW kernel module which has its own way of managing
+ * keys.
+ *
+ * key length should be between 32 and 128 bytes long
+ */
+int evm_set_key(void *key, size_t keylen)
+{
+ int rc;
+
+ rc = -EBUSY;
+ if (test_and_set_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags))
+ goto busy;
+ rc = -EINVAL;
+ if (keylen > MAX_KEY_SIZE)
+ goto inval;
+ memcpy(evmkey, key, keylen);
+ evm_initialized |= EVM_INIT_HMAC;
+ pr_info("key initialized\n");
+ return 0;
+inval:
+ clear_bit(EVM_SET_KEY_BUSY, &evm_set_key_flags);
+busy:
+ pr_err("key initialization failed\n");
+ return rc;
+}
+EXPORT_SYMBOL_GPL(evm_set_key);
+
static struct shash_desc *init_desc(char type)
{
long rc;
@@ -40,6 +79,10 @@ static struct shash_desc *init_desc(char type)
struct shash_desc *desc;
if (type == EVM_XATTR_HMAC) {
+ if (!(evm_initialized & EVM_INIT_HMAC)) {
+ pr_err("HMAC key is not set\n");
+ return ERR_PTR(-ENOKEY);
+ }
tfm = &hmac_tfm;
algo = evm_hmac;
} else {
@@ -240,7 +283,7 @@ int evm_init_key(void)
{
struct key *evm_key;
struct encrypted_key_payload *ekp;
- int rc = 0;
+ int rc;
evm_key = request_key(&key_type_encrypted, EVMKEY, NULL);
if (IS_ERR(evm_key))
@@ -248,12 +291,9 @@ int evm_init_key(void)
down_read(&evm_key->sem);
ekp = evm_key->payload.data[0];
- if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
- rc = -EINVAL;
- goto out;
- }
- memcpy(evmkey, ekp->decrypted_data, ekp->decrypted_datalen);
-out:
+
+ rc = evm_set_key(ekp->decrypted_data, ekp->decrypted_datalen);
+
/* burn the original key contents */
memset(ekp->decrypted_data, 0, ekp->decrypted_datalen);
up_read(&evm_key->sem);
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 1334e02..f716025 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -358,6 +358,15 @@ int evm_inode_removexattr(struct dentry *dentry, const char *xattr_name)
return evm_protect_xattr(dentry, xattr_name, NULL, 0);
}
+static void evm_reset_status(struct inode *inode)
+{
+ struct integrity_iint_cache *iint;
+
+ iint = integrity_iint_find(inode);
+ if (iint)
+ iint->evm_status = INTEGRITY_UNKNOWN;
+}
+
/**
* evm_inode_post_setxattr - update 'security.evm' to reflect the changes
* @dentry: pointer to the affected dentry
@@ -378,6 +387,8 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
&& !posix_xattr_acl(xattr_name)))
return;
+ evm_reset_status(dentry->d_inode);
+
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
}
@@ -396,6 +407,8 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
if (!evm_initialized || !evm_protected_xattr(xattr_name))
return;
+ evm_reset_status(dentry->d_inode);
+
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}
@@ -472,21 +485,34 @@ out:
}
EXPORT_SYMBOL_GPL(evm_inode_init_security);
+#ifdef CONFIG_EVM_LOAD_X509
+void __init evm_load_x509(void)
+{
+ int rc;
+
+ rc = integrity_load_x509(INTEGRITY_KEYRING_EVM, CONFIG_EVM_X509_PATH);
+ if (!rc)
+ evm_initialized |= EVM_INIT_X509;
+}
+#endif
+
static int __init init_evm(void)
{
int error;
evm_init_config();
+ error = integrity_init_keyring(INTEGRITY_KEYRING_EVM);
+ if (error)
+ return error;
+
error = evm_init_secfs();
if (error < 0) {
pr_info("Error registering secfs\n");
- goto err;
+ return error;
}
return 0;
-err:
- return error;
}
/*
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index cf12a04..c8dccd5 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -62,9 +62,9 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char temp[80];
- int i, error;
+ int i;
- if (!capable(CAP_SYS_ADMIN) || evm_initialized)
+ if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_INIT_HMAC))
return -EPERM;
if (count >= sizeof(temp) || count == 0)
@@ -78,12 +78,8 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf,
if ((sscanf(temp, "%d", &i) != 1) || (i != 1))
return -EINVAL;
- error = evm_init_key();
- if (!error) {
- evm_initialized = 1;
- pr_info("initialized\n");
- } else
- pr_err("initialization failed\n");
+ evm_init_key();
+
return count;
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 3d2f5b4..2de9c82 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -254,4 +254,5 @@ out:
void __init integrity_load_keys(void)
{
ima_load_x509();
+ evm_load_x509();
}
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index df30334..e54a8a8 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -107,6 +107,27 @@ config IMA_DEFAULT_HASH
default "sha512" if IMA_DEFAULT_HASH_SHA512
default "wp512" if IMA_DEFAULT_HASH_WP512
+config IMA_WRITE_POLICY
+ bool "Enable multiple writes to the IMA policy"
+ depends on IMA
+ default n
+ help
+ IMA policy can now be updated multiple times. The new rules get
+ appended to the original policy. Have in mind that the rules are
+ scanned in FIFO order so be careful when you design and add new ones.
+
+ If unsure, say N.
+
+config IMA_READ_POLICY
+ bool "Enable reading back the current IMA policy"
+ depends on IMA
+ default y if IMA_WRITE_POLICY
+ default n if !IMA_WRITE_POLICY
+ help
+ It is often useful to be able to read back the IMA policy. It is
+ even more important after introducing CONFIG_IMA_WRITE_POLICY.
+ This option allows the root user to see the current policy rules.
+
config IMA_APPRAISE
bool "Appraise integrity measurements"
depends on IMA
@@ -123,14 +144,35 @@ config IMA_APPRAISE
If unsure, say N.
config IMA_TRUSTED_KEYRING
- bool "Require all keys on the .ima keyring be signed"
+ bool "Require all keys on the .ima keyring be signed (deprecated)"
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
depends on INTEGRITY_ASYMMETRIC_KEYS
+ select INTEGRITY_TRUSTED_KEYRING
default y
help
This option requires that all keys added to the .ima
keyring be signed by a key on the system trusted keyring.
+ This option is deprecated in favor of INTEGRITY_TRUSTED_KEYRING
+
+config IMA_MOK_KEYRING
+ bool "Create IMA machine owner keys (MOK) and blacklist keyrings"
+ depends on SYSTEM_TRUSTED_KEYRING
+ depends on IMA_TRUSTED_KEYRING
+ default n
+ help
+ This option creates IMA MOK and blacklist keyrings. IMA MOK is an
+ intermediate keyring that sits between .system and .ima keyrings,
+ effectively forming a simple CA hierarchy. To successfully import a
+ key into .ima_mok it must be signed by a key which CA is in .system
+ keyring. On turn any key that needs to go in .ima keyring must be
+ signed by CA in either .system or .ima_mok keyrings. IMA MOK is empty
+ at kernel boot.
+
+ IMA blacklist keyring contains all revoked IMA keys. It is consulted
+ before any other keyring. If the search is successful the requested
+ operation is rejected and error is returned to the caller.
+
config IMA_LOAD_X509
bool "Load X509 certificate onto the '.ima' trusted keyring"
depends on IMA_TRUSTED_KEYRING
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index d79263d..a8539f9 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
+obj-$(CONFIG_IMA_MOK_KEYRING) += ima_mok.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e2a60c3..585af61 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -166,6 +166,11 @@ void ima_update_policy(void);
void ima_update_policy_flag(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
+int ima_check_policy(void);
+void *ima_policy_start(struct seq_file *m, loff_t *pos);
+void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
+void ima_policy_stop(struct seq_file *m, void *v);
+int ima_policy_show(struct seq_file *m, void *v);
/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01
@@ -250,17 +255,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
{
return -EINVAL;
}
-#endif /* CONFIG_IMA_LSM_RULES */
+#endif /* CONFIG_IMA_TRUSTED_KEYRING */
-#ifdef CONFIG_IMA_TRUSTED_KEYRING
-static inline int ima_init_keyring(const unsigned int id)
-{
- return integrity_init_keyring(id);
-}
+#ifdef CONFIG_IMA_READ_POLICY
+#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR)
#else
-static inline int ima_init_keyring(const unsigned int id)
-{
- return 0;
-}
-#endif /* CONFIG_IMA_TRUSTED_KEYRING */
-#endif
+#define POLICY_FILE_FLAGS S_IWUSR
+#endif /* CONFIG_IMA_WRITE_POLICY */
+
+#endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 816d175..3caed6d 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,6 +25,8 @@
#include "ima.h"
+static DEFINE_MUTEX(ima_write_mutex);
+
static int valid_policy = 1;
#define TMPBUFLEN 12
static ssize_t ima_show_htable_value(char __user *buf, size_t count,
@@ -261,6 +263,11 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
{
char *data = NULL;
ssize_t result;
+ int res;
+
+ res = mutex_lock_interruptible(&ima_write_mutex);
+ if (res)
+ return res;
if (datalen >= PAGE_SIZE)
datalen = PAGE_SIZE - 1;
@@ -286,6 +293,8 @@ out:
if (result < 0)
valid_policy = 0;
kfree(data);
+ mutex_unlock(&ima_write_mutex);
+
return result;
}
@@ -302,14 +311,31 @@ enum ima_fs_flags {
static unsigned long ima_fs_flags;
+#ifdef CONFIG_IMA_READ_POLICY
+static const struct seq_operations ima_policy_seqops = {
+ .start = ima_policy_start,
+ .next = ima_policy_next,
+ .stop = ima_policy_stop,
+ .show = ima_policy_show,
+};
+#endif
+
/*
* ima_open_policy: sequentialize access to the policy file
*/
static int ima_open_policy(struct inode *inode, struct file *filp)
{
- /* No point in being allowed to open it if you aren't going to write */
- if (!(filp->f_flags & O_WRONLY))
+ if (!(filp->f_flags & O_WRONLY)) {
+#ifndef CONFIG_IMA_READ_POLICY
return -EACCES;
+#else
+ if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
+ return -EACCES;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return seq_open(filp, &ima_policy_seqops);
+#endif
+ }
if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
return -EBUSY;
return 0;
@@ -326,6 +352,14 @@ static int ima_release_policy(struct inode *inode, struct file *file)
{
const char *cause = valid_policy ? "completed" : "failed";
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+ return 0;
+
+ if (valid_policy && ima_check_policy() < 0) {
+ cause = "failed";
+ valid_policy = 0;
+ }
+
pr_info("IMA: policy update %s\n", cause);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", cause, !valid_policy, 0);
@@ -336,15 +370,21 @@ static int ima_release_policy(struct inode *inode, struct file *file)
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
return 0;
}
+
ima_update_policy();
+#ifndef CONFIG_IMA_WRITE_POLICY
securityfs_remove(ima_policy);
ima_policy = NULL;
+#else
+ clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+#endif
return 0;
}
static const struct file_operations ima_measure_policy_ops = {
.open = ima_open_policy,
.write = ima_write_policy,
+ .read = seq_read,
.release = ima_release_policy,
.llseek = generic_file_llseek,
};
@@ -382,8 +422,7 @@ int __init ima_fs_init(void)
if (IS_ERR(violations))
goto out;
- ima_policy = securityfs_create_file("policy",
- S_IWUSR,
+ ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
if (IS_ERR(ima_policy))
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index e600cad..bd79f25 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -116,7 +116,7 @@ int __init ima_init(void)
if (!ima_used_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n");
- rc = ima_init_keyring(INTEGRITY_KEYRING_IMA);
+ rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc)
return rc;
diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c
new file mode 100644
index 0000000..676885e
--- /dev/null
+++ b/security/integrity/ima/ima_mok.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 Juniper Networks, Inc.
+ *
+ * Author:
+ * Petko Manolov <petko.manolov@konsulko.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <keys/asymmetric-type.h>
+
+
+struct key *ima_mok_keyring;
+struct key *ima_blacklist_keyring;
+
+/*
+ * Allocate the IMA MOK and blacklist keyrings
+ */
+__init int ima_mok_init(void)
+{
+ pr_notice("Allocating IMA MOK and blacklist keyrings.\n");
+
+ ima_mok_keyring = keyring_alloc(".ima_mok",
+ KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+
+ ima_blacklist_keyring = keyring_alloc(".ima_blacklist",
+ KUIDT_INIT(0), KGIDT_INIT(0), current_cred(),
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+
+ if (IS_ERR(ima_mok_keyring) || IS_ERR(ima_blacklist_keyring))
+ panic("Can't allocate IMA MOK or blacklist keyrings.");
+ set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_mok_keyring->flags);
+
+ set_bit(KEY_FLAG_TRUSTED_ONLY, &ima_blacklist_keyring->flags);
+ set_bit(KEY_FLAG_KEEP, &ima_blacklist_keyring->flags);
+ return 0;
+}
+device_initcall(ima_mok_init);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 3997e20..0a3b781 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -16,7 +16,9 @@
#include <linux/magic.h>
#include <linux/parser.h>
#include <linux/slab.h>
+#include <linux/rculist.h>
#include <linux/genhd.h>
+#include <linux/seq_file.h>
#include "ima.h"
@@ -38,6 +40,7 @@
#define AUDIT 0x0040
int ima_policy_flag;
+static int temp_ima_appraise;
#define MAX_LSM_RULES 6
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
@@ -135,11 +138,11 @@ static struct ima_rule_entry default_appraise_rules[] = {
static LIST_HEAD(ima_default_rules);
static LIST_HEAD(ima_policy_rules);
+static LIST_HEAD(ima_temp_rules);
static struct list_head *ima_rules;
-static DEFINE_MUTEX(ima_rules_mutex);
-
static int ima_policy __initdata;
+
static int __init default_measure_policy_setup(char *str)
{
if (ima_policy)
@@ -171,21 +174,18 @@ static int __init default_appraise_policy_setup(char *str)
__setup("ima_appraise_tcb", default_appraise_policy_setup);
/*
- * Although the IMA policy does not change, the LSM policy can be
- * reloaded, leaving the IMA LSM based rules referring to the old,
- * stale LSM policy.
- *
- * Update the IMA LSM based rules to reflect the reloaded LSM policy.
- * We assume the rules still exist; and BUG_ON() if they don't.
+ * The LSM policy can be reloaded, leaving the IMA LSM based rules referring
+ * to the old, stale LSM policy. Update the IMA LSM based rules to reflect
+ * the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
+ * they don't.
*/
static void ima_lsm_update_rules(void)
{
- struct ima_rule_entry *entry, *tmp;
+ struct ima_rule_entry *entry;
int result;
int i;
- mutex_lock(&ima_rules_mutex);
- list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ list_for_each_entry(entry, &ima_policy_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++) {
if (!entry->lsm[i].rule)
continue;
@@ -196,7 +196,6 @@ static void ima_lsm_update_rules(void)
BUG_ON(!entry->lsm[i].rule);
}
}
- mutex_unlock(&ima_rules_mutex);
}
/**
@@ -319,9 +318,9 @@ static int get_subaction(struct ima_rule_entry *rule, int func)
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
*
- * (There is no need for locking when walking the policy list,
- * as elements in the list are never deleted, nor does the list
- * change.)
+ * Since the IMA policy may be updated multiple times we need to lock the
+ * list when walking it. Reads are many orders of magnitude more numerous
+ * than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags)
@@ -329,7 +328,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
- list_for_each_entry(entry, ima_rules, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, ima_rules, list) {
if (!(entry->action & actmask))
continue;
@@ -351,6 +351,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
if (!actmask)
break;
}
+ rcu_read_unlock();
return action;
}
@@ -365,12 +366,12 @@ void ima_update_policy_flag(void)
{
struct ima_rule_entry *entry;
- ima_policy_flag = 0;
list_for_each_entry(entry, ima_rules, list) {
if (entry->action & IMA_DO_MASK)
ima_policy_flag |= entry->action;
}
+ ima_appraise |= temp_ima_appraise;
if (!ima_appraise)
ima_policy_flag &= ~IMA_APPRAISE;
}
@@ -415,16 +416,48 @@ void __init ima_init_policy(void)
ima_rules = &ima_default_rules;
}
+/* Make sure we have a valid policy, at least containing some rules. */
+int ima_check_policy()
+{
+ if (list_empty(&ima_temp_rules))
+ return -EINVAL;
+ return 0;
+}
+
/**
* ima_update_policy - update default_rules with new measure rules
*
* Called on file .release to update the default rules with a complete new
- * policy. Once updated, the policy is locked, no additional rules can be
- * added to the policy.
+ * policy. What we do here is to splice ima_policy_rules and ima_temp_rules so
+ * they make a queue. The policy may be updated multiple times and this is the
+ * RCU updater.
+ *
+ * Policy rules are never deleted so ima_policy_flag gets zeroed only once when
+ * we switch from the default policy to user defined.
*/
void ima_update_policy(void)
{
- ima_rules = &ima_policy_rules;
+ struct list_head *first, *last, *policy;
+
+ /* append current policy with the new rules */
+ first = (&ima_temp_rules)->next;
+ last = (&ima_temp_rules)->prev;
+ policy = &ima_policy_rules;
+
+ synchronize_rcu();
+
+ last->next = policy;
+ rcu_assign_pointer(list_next_rcu(policy->prev), first);
+ first->prev = policy->prev;
+ policy->prev = last;
+
+ /* prepare for the next policy rules addition */
+ INIT_LIST_HEAD(&ima_temp_rules);
+
+ if (ima_rules != policy) {
+ ima_policy_flag = 0;
+ ima_rules = policy;
+ }
ima_update_policy_flag();
}
@@ -436,8 +469,8 @@ enum {
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic,
- Opt_uid, Opt_euid, Opt_fowner,
- Opt_appraise_type, Opt_fsuuid, Opt_permit_directio
+ Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
+ Opt_appraise_type, Opt_permit_directio
};
static match_table_t policy_tokens = {
@@ -734,9 +767,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (!result && (entry->action == UNKNOWN))
result = -EINVAL;
else if (entry->func == MODULE_CHECK)
- ima_appraise |= IMA_APPRAISE_MODULES;
+ temp_ima_appraise |= IMA_APPRAISE_MODULES;
else if (entry->func == FIRMWARE_CHECK)
- ima_appraise |= IMA_APPRAISE_FIRMWARE;
+ temp_ima_appraise |= IMA_APPRAISE_FIRMWARE;
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -746,7 +779,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
* ima_parse_add_rule - add a rule to ima_policy_rules
* @rule - ima measurement policy rule
*
- * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Avoid locking by allowing just one writer at a time in ima_write_policy()
* Returns the length of the rule parsed, an error code on failure
*/
ssize_t ima_parse_add_rule(char *rule)
@@ -782,26 +815,230 @@ ssize_t ima_parse_add_rule(char *rule)
return result;
}
- mutex_lock(&ima_rules_mutex);
- list_add_tail(&entry->list, &ima_policy_rules);
- mutex_unlock(&ima_rules_mutex);
+ list_add_tail(&entry->list, &ima_temp_rules);
return len;
}
-/* ima_delete_rules called to cleanup invalid policy */
+/**
+ * ima_delete_rules() called to cleanup invalid in-flight policy.
+ * We don't need locking as we operate on the temp list, which is
+ * different from the active one. There is also only one user of
+ * ima_delete_rules() at a time.
+ */
void ima_delete_rules(void)
{
struct ima_rule_entry *entry, *tmp;
int i;
- mutex_lock(&ima_rules_mutex);
- list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+ temp_ima_appraise = 0;
+ list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
for (i = 0; i < MAX_LSM_RULES; i++)
kfree(entry->lsm[i].args_p);
list_del(&entry->list);
kfree(entry);
}
- mutex_unlock(&ima_rules_mutex);
}
+
+#ifdef CONFIG_IMA_READ_POLICY
+enum {
+ mask_exec = 0, mask_write, mask_read, mask_append
+};
+
+static char *mask_tokens[] = {
+ "MAY_EXEC",
+ "MAY_WRITE",
+ "MAY_READ",
+ "MAY_APPEND"
+};
+
+enum {
+ func_file = 0, func_mmap, func_bprm,
+ func_module, func_firmware, func_post
+};
+
+static char *func_tokens[] = {
+ "FILE_CHECK",
+ "MMAP_CHECK",
+ "BPRM_CHECK",
+ "MODULE_CHECK",
+ "FIRMWARE_CHECK",
+ "POST_SETATTR"
+};
+
+void *ima_policy_start(struct seq_file *m, loff_t *pos)
+{
+ loff_t l = *pos;
+ struct ima_rule_entry *entry;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, ima_rules, list) {
+ if (!l--) {
+ rcu_read_unlock();
+ return entry;
+ }
+ }
+ rcu_read_unlock();
+ return NULL;
+}
+
+void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct ima_rule_entry *entry = v;
+
+ rcu_read_lock();
+ entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (&entry->list == ima_rules) ? NULL : entry;
+}
+
+void ima_policy_stop(struct seq_file *m, void *v)
+{
+}
+
+#define pt(token) policy_tokens[token + Opt_err].pattern
+#define mt(token) mask_tokens[token]
+#define ft(token) func_tokens[token]
+
+int ima_policy_show(struct seq_file *m, void *v)
+{
+ struct ima_rule_entry *entry = v;
+ int i = 0;
+ char tbuf[64] = {0,};
+
+ rcu_read_lock();
+
+ if (entry->action & MEASURE)
+ seq_puts(m, pt(Opt_measure));
+ if (entry->action & DONT_MEASURE)
+ seq_puts(m, pt(Opt_dont_measure));
+ if (entry->action & APPRAISE)
+ seq_puts(m, pt(Opt_appraise));
+ if (entry->action & DONT_APPRAISE)
+ seq_puts(m, pt(Opt_dont_appraise));
+ if (entry->action & AUDIT)
+ seq_puts(m, pt(Opt_audit));
+
+ seq_puts(m, " ");
+
+ if (entry->flags & IMA_FUNC) {
+ switch (entry->func) {
+ case FILE_CHECK:
+ seq_printf(m, pt(Opt_func), ft(func_file));
+ break;
+ case MMAP_CHECK:
+ seq_printf(m, pt(Opt_func), ft(func_mmap));
+ break;
+ case BPRM_CHECK:
+ seq_printf(m, pt(Opt_func), ft(func_bprm));
+ break;
+ case MODULE_CHECK:
+ seq_printf(m, pt(Opt_func), ft(func_module));
+ break;
+ case FIRMWARE_CHECK:
+ seq_printf(m, pt(Opt_func), ft(func_firmware));
+ break;
+ case POST_SETATTR:
+ seq_printf(m, pt(Opt_func), ft(func_post));
+ break;
+ default:
+ snprintf(tbuf, sizeof(tbuf), "%d", entry->func);
+ seq_printf(m, pt(Opt_func), tbuf);
+ break;
+ }
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_MASK) {
+ if (entry->mask & MAY_EXEC)
+ seq_printf(m, pt(Opt_mask), mt(mask_exec));
+ if (entry->mask & MAY_WRITE)
+ seq_printf(m, pt(Opt_mask), mt(mask_write));
+ if (entry->mask & MAY_READ)
+ seq_printf(m, pt(Opt_mask), mt(mask_read));
+ if (entry->mask & MAY_APPEND)
+ seq_printf(m, pt(Opt_mask), mt(mask_append));
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_FSMAGIC) {
+ snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic);
+ seq_printf(m, pt(Opt_fsmagic), tbuf);
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_FSUUID) {
+ seq_puts(m, "fsuuid=");
+ for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) {
+ switch (i) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ seq_puts(m, "-");
+ }
+ seq_printf(m, "%x", entry->fsuuid[i]);
+ }
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_UID) {
+ snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+ seq_printf(m, pt(Opt_uid), tbuf);
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_EUID) {
+ snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+ seq_printf(m, pt(Opt_euid), tbuf);
+ seq_puts(m, " ");
+ }
+
+ if (entry->flags & IMA_FOWNER) {
+ snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
+ seq_printf(m, pt(Opt_fowner), tbuf);
+ seq_puts(m, " ");
+ }
+
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ if (entry->lsm[i].rule) {
+ switch (i) {
+ case LSM_OBJ_USER:
+ seq_printf(m, pt(Opt_obj_user),
+ (char *)entry->lsm[i].args_p);
+ break;
+ case LSM_OBJ_ROLE:
+ seq_printf(m, pt(Opt_obj_role),
+ (char *)entry->lsm[i].args_p);
+ break;
+ case LSM_OBJ_TYPE:
+ seq_printf(m, pt(Opt_obj_type),
+ (char *)entry->lsm[i].args_p);
+ break;
+ case LSM_SUBJ_USER:
+ seq_printf(m, pt(Opt_subj_user),
+ (char *)entry->lsm[i].args_p);
+ break;
+ case LSM_SUBJ_ROLE:
+ seq_printf(m, pt(Opt_subj_role),
+ (char *)entry->lsm[i].args_p);
+ break;
+ case LSM_SUBJ_TYPE:
+ seq_printf(m, pt(Opt_subj_type),
+ (char *)entry->lsm[i].args_p);
+ break;
+ }
+ }
+ }
+ if (entry->flags & IMA_DIGSIG_REQUIRED)
+ seq_puts(m, "appraise_type=imasig ");
+ if (entry->flags & IMA_PERMIT_DIRECTIO)
+ seq_puts(m, "permit_directio ");
+ rcu_read_unlock();
+ seq_puts(m, "\n");
+ return 0;
+}
+#endif /* CONFIG_IMA_READ_POLICY */
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 9c61687..5efe2ec 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -125,8 +125,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
int __init integrity_read_file(const char *path, char **data);
#define INTEGRITY_KEYRING_EVM 0
-#define INTEGRITY_KEYRING_MODULE 1
-#define INTEGRITY_KEYRING_IMA 2
+#define INTEGRITY_KEYRING_IMA 1
+#define INTEGRITY_KEYRING_MODULE 2
#define INTEGRITY_KEYRING_MAX 3
#ifdef CONFIG_INTEGRITY_SIGNATURE
@@ -149,7 +149,6 @@ static inline int integrity_init_keyring(const unsigned int id)
{
return 0;
}
-
#endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
@@ -171,6 +170,14 @@ static inline void ima_load_x509(void)
}
#endif
+#ifdef CONFIG_EVM_LOAD_X509
+void __init evm_load_x509(void);
+#else
+static inline void evm_load_x509(void)
+{
+}
+#endif
+
#ifdef CONFIG_INTEGRITY_AUDIT
/* declarations */
void integrity_audit_msg(int audit_msgno, struct inode *inode,
diff --git a/security/keys/key.c b/security/keys/key.c
index ab7997d..09ef276 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -429,8 +429,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 fb111ea..e83ec6b 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,13 @@ long keyctl_revoke_key(key_serial_t id)
}
}
- key_revoke(key_ref_to_ptr(key_ref));
- ret = 0;
+ key = key_ref_to_ptr(key_ref);
+ if (test_bit(KEY_FLAG_KEEP, &key->flags))
+ return -EPERM;
+ else {
+ key_revoke(key);
+ ret = 0;
+ }
key_ref_put(key_ref);
error:
@@ -392,11 +400,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 +431,13 @@ long keyctl_invalidate_key(key_serial_t id)
}
invalidate:
- key_invalidate(key_ref_to_ptr(key_ref));
- ret = 0;
+ key = key_ref_to_ptr(key_ref);
+ if (test_bit(KEY_FLAG_KEEP, &key->flags))
+ ret = -EPERM;
+ else {
+ key_invalidate(key);
+ ret = 0;
+ }
error_put:
key_ref_put(key_ref);
error:
@@ -433,12 +449,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 +477,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 +532,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 +554,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 +1319,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 +1352,14 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
okay:
key = key_ref_to_ptr(key_ref);
- key_set_timeout(key, timeout);
+ if (test_bit(KEY_FLAG_KEEP, &key->flags))
+ ret = -EPERM;
+ else {
+ key_set_timeout(key, timeout);
+ ret = 0;
+ }
key_put(key);
- ret = 0;
error:
return ret;
}
OpenPOWER on IntegriCloud