diff options
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/Kconfig | 11 | ||||
-rw-r--r-- | security/integrity/digsig.c | 14 | ||||
-rw-r--r-- | security/integrity/digsig_asymmetric.c | 14 | ||||
-rw-r--r-- | security/integrity/evm/Kconfig | 17 | ||||
-rw-r--r-- | security/integrity/evm/evm.h | 3 | ||||
-rw-r--r-- | security/integrity/evm/evm_crypto.c | 54 | ||||
-rw-r--r-- | security/integrity/evm/evm_main.c | 32 | ||||
-rw-r--r-- | security/integrity/evm/evm_secfs.c | 12 | ||||
-rw-r--r-- | security/integrity/iint.c | 1 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 44 | ||||
-rw-r--r-- | security/integrity/ima/Makefile | 1 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 24 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 53 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima_mok.c | 55 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 301 | ||||
-rw-r--r-- | security/integrity/integrity.h | 13 |
17 files changed, 575 insertions, 76 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 73c457bf5a4a..21d756832b75 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 5be9ffbe90ba..8ef15118cc78 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 4fec1816a2b3..5ade2a7517a6 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 bf19723cf117..e825e0ae78e7 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 88bfe77efa1c..f5f12727771a 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 461f8d891579..30b6b7d0429f 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 1334e02ae8f4..f7160253f17f 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 cf12a04717d3..c8dccd54d501 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 c2e3ccd4b510..8f1ab37f2897 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -255,4 +255,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 df303346029b..e54a8a8dae94 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 d79263d2fdbf..a8539f9e060f 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 e2a60c30df44..585af61ed399 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 816d175da79a..f355231997b4 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, @@ -259,7 +261,7 @@ static const struct file_operations ima_ascii_measurements_ops = { static ssize_t ima_write_policy(struct file *file, const char __user *buf, size_t datalen, loff_t *ppos) { - char *data = NULL; + char *data; ssize_t result; if (datalen >= PAGE_SIZE) @@ -279,13 +281,20 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, result = -EFAULT; if (copy_from_user(data, buf, datalen)) - goto out; + goto out_free; + result = mutex_lock_interruptible(&ima_write_mutex); + if (result < 0) + goto out_free; result = ima_parse_add_rule(data); + mutex_unlock(&ima_write_mutex); + +out_free: + kfree(data); out: if (result < 0) valid_policy = 0; - kfree(data); + 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 e600cadd231c..bd79f254d204 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 000000000000..676885e4320e --- /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 3997e206f82d..0a3b781f18e5 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 9c6168709d3b..5efe2ecc538d 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, |