diff options
Diffstat (limited to 'security/integrity/evm')
| -rw-r--r-- | security/integrity/evm/Kconfig | 45 | ||||
| -rw-r--r-- | security/integrity/evm/evm.h | 39 | ||||
| -rw-r--r-- | security/integrity/evm/evm_crypto.c | 93 | ||||
| -rw-r--r-- | security/integrity/evm/evm_main.c | 153 | ||||
| -rw-r--r-- | security/integrity/evm/evm_posix_acl.c | 3 | ||||
| -rw-r--r-- | security/integrity/evm/evm_secfs.c | 12 |
6 files changed, 266 insertions, 79 deletions
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index afbb59dd262..d606f3d12d6 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -1,13 +1,52 @@ config EVM boolean "EVM support" - depends on SECURITY && KEYS && (TRUSTED_KEYS=y || TRUSTED_KEYS=n) + depends on SECURITY + select KEYS + select ENCRYPTED_KEYS select CRYPTO_HMAC - select CRYPTO_MD5 select CRYPTO_SHA1 - select ENCRYPTED_KEYS default n help EVM protects a file's security extended attributes against integrity attacks. If you are unsure how to answer this question, answer N. + +if EVM + +menu "EVM options" + +config EVM_ATTR_FSUUID + bool "FSUUID (version 2)" + default y + depends on EVM + help + Include filesystem UUID for HMAC calculation. + + Default value is 'selected', which is former version 2. + if 'not selected', it is former version 1 + + WARNING: changing the HMAC calculation method or adding + additional info to the calculation, requires existing EVM + labeled file systems to be relabeled. + +config EVM_EXTRA_SMACK_XATTRS + bool "Additional SMACK xattrs" + depends on EVM && SECURITY_SMACK + default n + help + Include additional SMACK xattrs for HMAC calculation. + + In addition to the original security xattrs (eg. security.selinux, + security.SMACK64, security.capability, and security.ima) included + in the HMAC calculation, enabling this option includes newly defined + Smack xattrs: security.SMACK64EXEC, security.SMACK64TRANSMUTE and + security.SMACK64MMAP. + + WARNING: changing the HMAC calculation method or adding + additional info to the calculation, requires existing EVM + labeled file systems to be relabeled. + +endmenu + +endif diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index d320f519743..88bfe77efa1 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -12,27 +12,42 @@ * File: evm.h * */ + +#ifndef __INTEGRITY_EVM_H +#define __INTEGRITY_EVM_H + #include <linux/xattr.h> #include <linux/security.h> + #include "../integrity.h" extern int evm_initialized; extern char *evm_hmac; +extern char *evm_hash; + +#define EVM_ATTR_FSUUID 0x0001 + +extern int evm_hmac_attrs; extern struct crypto_shash *hmac_tfm; +extern struct crypto_shash *hash_tfm; /* List of EVM protected security xattrs */ extern char *evm_config_xattrnames[]; -extern int evm_init_key(void); -extern int evm_update_evmxattr(struct dentry *dentry, - const char *req_xattr_name, - const char *req_xattr_value, - size_t req_xattr_value_len); -extern int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, - const char *req_xattr_value, - size_t req_xattr_value_len, char *digest); -extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, - char *hmac_val); -extern int evm_init_secfs(void); -extern void evm_cleanup_secfs(void); +int evm_init_key(void); +int evm_update_evmxattr(struct dentry *dentry, + const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len); +int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len, char *digest); +int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len, char *digest); +int evm_init_hmac(struct inode *inode, const struct xattr *xattr, + char *hmac_val); +int evm_init_secfs(void); + +#endif diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index e76a470c3a8..5e9687f02e1 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -13,6 +13,8 @@ * Using root's kernel master key (kmk), calculate the HMAC */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/crypto.h> #include <linux/xattr.h> @@ -26,44 +28,56 @@ static unsigned char evmkey[MAX_KEY_SIZE]; static int evmkey_len = MAX_KEY_SIZE; struct crypto_shash *hmac_tfm; +struct crypto_shash *hash_tfm; static DEFINE_MUTEX(mutex); -static struct shash_desc *init_desc(void) +static struct shash_desc *init_desc(char type) { - int rc; + long rc; + char *algo; + struct crypto_shash **tfm; struct shash_desc *desc; - if (hmac_tfm == NULL) { + if (type == EVM_XATTR_HMAC) { + tfm = &hmac_tfm; + algo = evm_hmac; + } else { + tfm = &hash_tfm; + algo = evm_hash; + } + + if (*tfm == NULL) { mutex_lock(&mutex); - if (hmac_tfm) + if (*tfm) goto out; - hmac_tfm = crypto_alloc_shash(evm_hmac, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hmac_tfm)) { - pr_err("Can not allocate %s (reason: %ld)\n", - evm_hmac, PTR_ERR(hmac_tfm)); - rc = PTR_ERR(hmac_tfm); - hmac_tfm = NULL; + *tfm = crypto_alloc_shash(algo, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(*tfm)) { + rc = PTR_ERR(*tfm); + pr_err("Can not allocate %s (reason: %ld)\n", algo, rc); + *tfm = NULL; mutex_unlock(&mutex); return ERR_PTR(rc); } - rc = crypto_shash_setkey(hmac_tfm, evmkey, evmkey_len); - if (rc) { - crypto_free_shash(hmac_tfm); - hmac_tfm = NULL; - mutex_unlock(&mutex); - return ERR_PTR(rc); + if (type == EVM_XATTR_HMAC) { + rc = crypto_shash_setkey(*tfm, evmkey, evmkey_len); + if (rc) { + crypto_free_shash(*tfm); + *tfm = NULL; + mutex_unlock(&mutex); + return ERR_PTR(rc); + } } out: mutex_unlock(&mutex); } - desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(hmac_tfm), + desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(*tfm), GFP_KERNEL); if (!desc) return ERR_PTR(-ENOMEM); - desc->tfm = hmac_tfm; + desc->tfm = *tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; rc = crypto_shash_init(desc); @@ -91,13 +105,16 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, umode_t mode; } hmac_misc; - memset(&hmac_misc, 0, sizeof hmac_misc); + memset(&hmac_misc, 0, sizeof(hmac_misc)); hmac_misc.ino = inode->i_ino; hmac_misc.generation = inode->i_generation; - hmac_misc.uid = inode->i_uid; - hmac_misc.gid = inode->i_gid; + hmac_misc.uid = from_kuid(&init_user_ns, inode->i_uid); + hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); hmac_misc.mode = inode->i_mode; - crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); + crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc)); + if (evm_hmac_attrs & EVM_ATTR_FSUUID) + crypto_shash_update(desc, inode->i_sb->s_uuid, + sizeof(inode->i_sb->s_uuid)); crypto_shash_final(desc, digest); } @@ -108,9 +125,11 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, * the hmac using the requested xattr value. Don't alloc/free memory for * each xattr, but attempt to re-use the previously allocated memory. */ -int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, - const char *req_xattr_value, size_t req_xattr_value_len, - char *digest) +static int evm_calc_hmac_or_hash(struct dentry *dentry, + const char *req_xattr_name, + const char *req_xattr_value, + size_t req_xattr_value_len, + char type, char *digest) { struct inode *inode = dentry->d_inode; struct shash_desc *desc; @@ -120,9 +139,9 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, int error; int size; - if (!inode->i_op || !inode->i_op->getxattr) + if (!inode->i_op->getxattr) return -EOPNOTSUPP; - desc = init_desc(); + desc = init_desc(type); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -156,6 +175,22 @@ out: return error; } +int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, size_t req_xattr_value_len, + char *digest) +{ + return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, + req_xattr_value_len, EVM_XATTR_HMAC, digest); +} + +int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, + const char *req_xattr_value, size_t req_xattr_value_len, + char *digest) +{ + return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value, + req_xattr_value_len, IMA_XATTR_DIGEST, digest); +} + /* * Calculate the hmac and update security.evm xattr * @@ -186,9 +221,9 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc; - desc = init_desc(); + desc = init_desc(EVM_XATTR_HMAC); if (IS_ERR(desc)) { - printk(KERN_INFO "init_desc failed\n"); + pr_info("init_desc failed\n"); return PTR_ERR(desc); } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 92d3d99a9f7..3bcb80df4d0 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -14,8 +14,11 @@ * evm_inode_removexattr, and evm_verifyxattr */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/crypto.h> +#include <linux/audit.h> #include <linux/xattr.h> #include <linux/integrity.h> #include <linux/evm.h> @@ -24,7 +27,12 @@ int evm_initialized; +static char *integrity_status_msg[] = { + "pass", "fail", "no_label", "no_xattrs", "unknown" +}; char *evm_hmac = "hmac(sha1)"; +char *evm_hash = "sha1"; +int evm_hmac_attrs; char *evm_config_xattrnames[] = { #ifdef CONFIG_SECURITY_SELINUX @@ -32,6 +40,14 @@ char *evm_config_xattrnames[] = { #endif #ifdef CONFIG_SECURITY_SMACK XATTR_NAME_SMACK, +#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS + XATTR_NAME_SMACKEXEC, + XATTR_NAME_SMACKTRANSMUTE, + XATTR_NAME_SMACKMMAP, +#endif +#endif +#ifdef CONFIG_IMA_APPRAISE + XATTR_NAME_IMA, #endif XATTR_NAME_CAPS, NULL @@ -46,6 +62,37 @@ static int __init evm_set_fixmode(char *str) } __setup("evm=", evm_set_fixmode); +static void __init evm_init_config(void) +{ +#ifdef CONFIG_EVM_ATTR_FSUUID + evm_hmac_attrs |= EVM_ATTR_FSUUID; +#endif + pr_info("HMAC attrs: 0x%x\n", evm_hmac_attrs); +} + +static int evm_find_protected_xattrs(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + char **xattr; + int error; + int count = 0; + + if (!inode->i_op->getxattr) + return -EOPNOTSUPP; + + for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) { + error = inode->i_op->getxattr(dentry, *xattr, NULL, 0); + if (error < 0) { + if (error == -ENODATA) + continue; + return error; + } + count++; + } + + return count; +} + /* * evm_verify_hmac - calculate and compare the HMAC with the EVM xattr * @@ -65,32 +112,72 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, size_t xattr_value_len, struct integrity_iint_cache *iint) { - struct evm_ima_xattr_data xattr_data; + struct evm_ima_xattr_data *xattr_data = NULL; + struct evm_ima_xattr_data calc; enum integrity_status evm_status = INTEGRITY_PASS; - int rc; + int rc, xattr_len; if (iint && iint->evm_status == INTEGRITY_PASS) return iint->evm_status; /* if status is not PASS, try to check again - against -ENOMEM */ - rc = evm_calc_hmac(dentry, xattr_name, xattr_value, - xattr_value_len, xattr_data.digest); - if (rc < 0) { - evm_status = (rc == -ENODATA) - ? INTEGRITY_NOXATTRS : INTEGRITY_FAIL; + /* first need to know the sig type */ + rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0, + GFP_NOFS); + if (rc <= 0) { + if (rc == 0) + evm_status = INTEGRITY_FAIL; /* empty */ + else if (rc == -ENODATA) { + rc = evm_find_protected_xattrs(dentry); + if (rc > 0) + evm_status = INTEGRITY_NOLABEL; + else if (rc == 0) + evm_status = INTEGRITY_NOXATTRS; /* new file */ + } goto out; } - xattr_data.type = EVM_XATTR_HMAC; - rc = vfs_xattr_cmp(dentry, XATTR_NAME_EVM, (u8 *)&xattr_data, - sizeof xattr_data, GFP_NOFS); - if (rc < 0) - evm_status = (rc == -ENODATA) - ? INTEGRITY_NOLABEL : INTEGRITY_FAIL; + xattr_len = rc; + + /* check value type */ + switch (xattr_data->type) { + case EVM_XATTR_HMAC: + rc = evm_calc_hmac(dentry, xattr_name, xattr_value, + xattr_value_len, calc.digest); + if (rc) + break; + rc = memcmp(xattr_data->digest, calc.digest, + sizeof(calc.digest)); + if (rc) + rc = -EINVAL; + break; + case EVM_IMA_XATTR_DIGSIG: + rc = evm_calc_hash(dentry, xattr_name, xattr_value, + xattr_value_len, calc.digest); + if (rc) + break; + rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM, + (const char *)xattr_data, xattr_len, + calc.digest, sizeof(calc.digest)); + if (!rc) { + /* we probably want to replace rsa with hmac here */ + evm_update_evmxattr(dentry, xattr_name, xattr_value, + xattr_value_len); + } + break; + default: + rc = -EINVAL; + break; + } + + if (rc) + evm_status = (rc == -ENODATA) ? + INTEGRITY_NOXATTRS : INTEGRITY_FAIL; out: if (iint) iint->evm_status = evm_status; + kfree(xattr_data); return evm_status; } @@ -194,9 +281,15 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, if ((evm_status == INTEGRITY_PASS) || (evm_status == INTEGRITY_NOXATTRS)) return 0; - return -EPERM; + goto out; } evm_status = evm_verify_current_integrity(dentry); +out: + if (evm_status != INTEGRITY_PASS) + integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, + dentry->d_name.name, "appraise_metadata", + integrity_status_msg[evm_status], + -EPERM, 0); return evm_status == INTEGRITY_PASS ? 0 : -EPERM; } @@ -207,12 +300,20 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, * @xattr_value: pointer to the new extended attribute value * @xattr_value_len: pointer to the new extended attribute value length * - * Updating 'security.evm' requires CAP_SYS_ADMIN privileges and that - * the current value is valid. + * Before allowing the 'security.evm' protected xattr to be updated, + * verify the existing value is valid. As only the kernel should have + * access to the EVM encrypted key needed to calculate the HMAC, prevent + * userspace from writing HMAC value. Writing 'security.evm' requires + * requires CAP_SYS_ADMIN privileges. */ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { + const struct evm_ima_xattr_data *xattr_data = xattr_value; + + if ((strcmp(xattr_name, XATTR_NAME_EVM) == 0) + && (xattr_data->type == EVM_XATTR_HMAC)) + return -EPERM; return evm_protect_xattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -289,6 +390,9 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) if ((evm_status == INTEGRITY_PASS) || (evm_status == INTEGRITY_NOXATTRS)) return 0; + integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, + dentry->d_name.name, "appraise_metadata", + integrity_status_msg[evm_status], -EPERM, 0); return -EPERM; } @@ -337,7 +441,7 @@ int evm_inode_init_security(struct inode *inode, evm_xattr->value = xattr_data; evm_xattr->value_len = sizeof(*xattr_data); - evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); + evm_xattr->name = XATTR_EVM_SUFFIX; return 0; out: kfree(xattr_data); @@ -349,22 +453,19 @@ static int __init init_evm(void) { int error; + evm_init_config(); + error = evm_init_secfs(); if (error < 0) { - printk(KERN_INFO "EVM: Error registering secfs\n"); + pr_info("Error registering secfs\n"); goto err; } + + return 0; err: return error; } -static void __exit cleanup_evm(void) -{ - evm_cleanup_secfs(); - if (hmac_tfm) - crypto_free_shash(hmac_tfm); -} - /* * evm_display_config - list the EVM protected security extended attributes */ @@ -373,7 +474,7 @@ static int __init evm_display_config(void) char **xattrname; for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) - printk(KERN_INFO "EVM: %s\n", *xattrname); + pr_info("%s\n", *xattrname); return 0; } diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c index b1753e98bf9..46408b9e62e 100644 --- a/security/integrity/evm/evm_posix_acl.c +++ b/security/integrity/evm/evm_posix_acl.c @@ -11,8 +11,9 @@ #include <linux/module.h> #include <linux/xattr.h> +#include <linux/evm.h> -int posix_xattr_acl(char *xattr) +int posix_xattr_acl(const char *xattr) { int xattr_len = strlen(xattr); diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index ac762995057..cf12a04717d 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -13,6 +13,8 @@ * - Get the key and enable EVM */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/uaccess.h> #include <linux/module.h> #include "evm.h" @@ -79,9 +81,9 @@ static ssize_t evm_write_key(struct file *file, const char __user *buf, error = evm_init_key(); if (!error) { evm_initialized = 1; - pr_info("EVM: initialized\n"); + pr_info("initialized\n"); } else - pr_err("EVM: initialization failed\n"); + pr_err("initialization failed\n"); return count; } @@ -100,9 +102,3 @@ int __init evm_init_secfs(void) error = -EFAULT; return error; } - -void __exit evm_cleanup_secfs(void) -{ - if (evm_init_tpm) - securityfs_remove(evm_init_tpm); -} |
