aboutsummaryrefslogtreecommitdiff
path: root/security/integrity/evm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-06-07 15:40:37 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-06-07 15:40:37 -0700
commit00d535a386c071a06e111fd846f6beda445126a5 (patch)
treef0ebe350b705281ba9816d9e023c2442a2bc0304 /security/integrity/evm
parent289cf155d95df07ac1e692dfaa7036f10235c77c (diff)
parentb5c90a7526fe39164c2204f0404ce8f8ff21e522 (diff)
Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull integrity updates from James Morris: "From Mimi: - add run time support for specifying additional security xattrs included in the security.evm HMAC/signature - some code clean up and bug fixes" * 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: EVM: unlock on error path in evm_read_xattrs() EVM: prevent array underflow in evm_write_xattrs() EVM: Fix null dereference on xattr when xattr fails to allocate EVM: fix memory leak of temporary buffer 'temp' IMA: use list_splice_tail_init_rcu() instead of its open coded variant ima: use match_string() helper ima: fix updating the ima_appraise flag ima: based on policy verify firmware signatures (pre-allocated buffer) ima: define a new policy condition based on the filesystem name EVM: Allow runtime modification of the set of verified xattrs EVM: turn evm_config_xattrnames into a list integrity: Add an integrity directory in securityfs ima: Remove unused variable ima_initialized ima: Unify logging ima: Reflect correct permissions for policy
Diffstat (limited to 'security/integrity/evm')
-rw-r--r--security/integrity/evm/Kconfig11
-rw-r--r--security/integrity/evm/evm.h7
-rw-r--r--security/integrity/evm/evm_crypto.c10
-rw-r--r--security/integrity/evm/evm_main.c79
-rw-r--r--security/integrity/evm/evm_secfs.c206
5 files changed, 271 insertions, 42 deletions
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index e825e0ae78e7..d593346d0bba 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -42,6 +42,17 @@ config EVM_EXTRA_SMACK_XATTRS
additional info to the calculation, requires existing EVM
labeled file systems to be relabeled.
+config EVM_ADD_XATTRS
+ bool "Add additional EVM extended attributes at runtime"
+ depends on EVM
+ default n
+ help
+ Allow userland to provide additional xattrs for HMAC calculation.
+
+ When this option is enabled, root can add additional xattrs to the
+ list used by EVM by writing them into
+ /sys/kernel/security/integrity/evm/evm_xattrs.
+
config EVM_LOAD_X509
bool "Load an X509 certificate onto the '.evm' trusted keyring"
depends on EVM && INTEGRITY_TRUSTED_KEYRING
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 45c4a89c02ff..1257c3c24723 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -30,6 +30,11 @@
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
EVM_ALLOW_METADATA_WRITES)
+struct xattr_list {
+ struct list_head list;
+ char *name;
+};
+
extern int evm_initialized;
#define EVM_ATTR_FSUUID 0x0001
@@ -40,7 +45,7 @@ extern struct crypto_shash *hmac_tfm;
extern struct crypto_shash *hash_tfm;
/* List of EVM protected security xattrs */
-extern char *evm_config_xattrnames[];
+extern struct list_head evm_config_xattrnames;
int evm_init_key(void);
int evm_update_evmxattr(struct dentry *dentry,
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index facf9cdd577d..b60524310855 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -192,8 +192,8 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
char type, char *digest)
{
struct inode *inode = d_backing_inode(dentry);
+ struct xattr_list *xattr;
struct shash_desc *desc;
- char **xattrname;
size_t xattr_size = 0;
char *xattr_value = NULL;
int error;
@@ -209,14 +209,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
return PTR_ERR(desc);
error = -ENODATA;
- for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
+ list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
bool is_ima = false;
- if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
+ if (strcmp(xattr->name, XATTR_NAME_IMA) == 0)
is_ima = true;
if ((req_xattr_name && req_xattr_value)
- && !strcmp(*xattrname, req_xattr_name)) {
+ && !strcmp(xattr->name, req_xattr_name)) {
error = 0;
crypto_shash_update(desc, (const u8 *)req_xattr_value,
req_xattr_value_len);
@@ -224,7 +224,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
ima_present = true;
continue;
}
- size = vfs_getxattr_alloc(dentry, *xattrname,
+ size = vfs_getxattr_alloc(dentry, xattr->name,
&xattr_value, xattr_size, GFP_NOFS);
if (size == -ENOMEM) {
error = -ENOMEM;
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 9ea9c19a545c..f9eff5041e4c 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -35,28 +35,29 @@ static const char * const integrity_status_msg[] = {
};
int evm_hmac_attrs;
-char *evm_config_xattrnames[] = {
+static struct xattr_list evm_config_default_xattrnames[] = {
#ifdef CONFIG_SECURITY_SELINUX
- XATTR_NAME_SELINUX,
+ {.name = XATTR_NAME_SELINUX},
#endif
#ifdef CONFIG_SECURITY_SMACK
- XATTR_NAME_SMACK,
+ {.name = XATTR_NAME_SMACK},
#ifdef CONFIG_EVM_EXTRA_SMACK_XATTRS
- XATTR_NAME_SMACKEXEC,
- XATTR_NAME_SMACKTRANSMUTE,
- XATTR_NAME_SMACKMMAP,
+ {.name = XATTR_NAME_SMACKEXEC},
+ {.name = XATTR_NAME_SMACKTRANSMUTE},
+ {.name = XATTR_NAME_SMACKMMAP},
#endif
#endif
#ifdef CONFIG_SECURITY_APPARMOR
- XATTR_NAME_APPARMOR,
+ {.name = XATTR_NAME_APPARMOR},
#endif
#ifdef CONFIG_IMA_APPRAISE
- XATTR_NAME_IMA,
+ {.name = XATTR_NAME_IMA},
#endif
- XATTR_NAME_CAPS,
- NULL
+ {.name = XATTR_NAME_CAPS},
};
+LIST_HEAD(evm_config_xattrnames);
+
static int evm_fixmode;
static int __init evm_set_fixmode(char *str)
{
@@ -68,6 +69,17 @@ __setup("evm=", evm_set_fixmode);
static void __init evm_init_config(void)
{
+ int i, xattrs;
+
+ xattrs = ARRAY_SIZE(evm_config_default_xattrnames);
+
+ pr_info("Initialising EVM extended attributes:\n");
+ for (i = 0; i < xattrs; i++) {
+ pr_info("%s\n", evm_config_default_xattrnames[i].name);
+ list_add_tail(&evm_config_default_xattrnames[i].list,
+ &evm_config_xattrnames);
+ }
+
#ifdef CONFIG_EVM_ATTR_FSUUID
evm_hmac_attrs |= EVM_ATTR_FSUUID;
#endif
@@ -82,15 +94,15 @@ static bool evm_key_loaded(void)
static int evm_find_protected_xattrs(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- char **xattr;
+ struct xattr_list *xattr;
int error;
int count = 0;
if (!(inode->i_opflags & IOP_XATTR))
return -EOPNOTSUPP;
- for (xattr = evm_config_xattrnames; *xattr != NULL; xattr++) {
- error = __vfs_getxattr(dentry, inode, *xattr, NULL, 0);
+ list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
+ error = __vfs_getxattr(dentry, inode, xattr->name, NULL, 0);
if (error < 0) {
if (error == -ENODATA)
continue;
@@ -211,24 +223,25 @@ out:
static int evm_protected_xattr(const char *req_xattr_name)
{
- char **xattrname;
int namelen;
int found = 0;
+ struct xattr_list *xattr;
namelen = strlen(req_xattr_name);
- for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
- if ((strlen(*xattrname) == namelen)
- && (strncmp(req_xattr_name, *xattrname, namelen) == 0)) {
+ list_for_each_entry_rcu(xattr, &evm_config_xattrnames, list) {
+ if ((strlen(xattr->name) == namelen)
+ && (strncmp(req_xattr_name, xattr->name, namelen) == 0)) {
found = 1;
break;
}
if (strncmp(req_xattr_name,
- *xattrname + XATTR_SECURITY_PREFIX_LEN,
+ xattr->name + XATTR_SECURITY_PREFIX_LEN,
strlen(req_xattr_name)) == 0) {
found = 1;
break;
}
}
+
return found;
}
@@ -544,35 +557,35 @@ void __init evm_load_x509(void)
static int __init init_evm(void)
{
int error;
+ struct list_head *pos, *q;
+ struct xattr_list *xattr;
evm_init_config();
error = integrity_init_keyring(INTEGRITY_KEYRING_EVM);
if (error)
- return error;
+ goto error;
error = evm_init_secfs();
if (error < 0) {
pr_info("Error registering secfs\n");
- return error;
+ goto error;
}
- return 0;
-}
-
-/*
- * evm_display_config - list the EVM protected security extended attributes
- */
-static int __init evm_display_config(void)
-{
- char **xattrname;
+error:
+ if (error != 0) {
+ if (!list_empty(&evm_config_xattrnames)) {
+ list_for_each_safe(pos, q, &evm_config_xattrnames) {
+ xattr = list_entry(pos, struct xattr_list,
+ list);
+ list_del(pos);
+ }
+ }
+ }
- for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++)
- pr_info("%s\n", *xattrname);
- return 0;
+ return error;
}
-pure_initcall(evm_display_config);
late_initcall(init_evm);
MODULE_DESCRIPTION("Extended Verification Module");
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index feba03bbedae..637eb999e340 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -15,11 +15,21 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/audit.h>
#include <linux/uaccess.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include "evm.h"
+static struct dentry *evm_dir;
static struct dentry *evm_init_tpm;
+static struct dentry *evm_symlink;
+
+#ifdef CONFIG_EVM_ADD_XATTRS
+static struct dentry *evm_xattrs;
+static DEFINE_MUTEX(xattr_list_mutex);
+static int evm_xattrs_locked;
+#endif
/**
* evm_read_key - read() for <securityfs>/evm
@@ -107,13 +117,203 @@ static const struct file_operations evm_key_ops = {
.write = evm_write_key,
};
+#ifdef CONFIG_EVM_ADD_XATTRS
+/**
+ * evm_read_xattrs - read() for <securityfs>/evm_xattrs
+ *
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *temp;
+ int offset = 0;
+ ssize_t rc, size = 0;
+ struct xattr_list *xattr;
+
+ if (*ppos != 0)
+ return 0;
+
+ rc = mutex_lock_interruptible(&xattr_list_mutex);
+ if (rc)
+ return -ERESTARTSYS;
+
+ list_for_each_entry(xattr, &evm_config_xattrnames, list)
+ size += strlen(xattr->name) + 1;
+
+ temp = kmalloc(size + 1, GFP_KERNEL);
+ if (!temp) {
+ mutex_unlock(&xattr_list_mutex);
+ return -ENOMEM;
+ }
+
+ list_for_each_entry(xattr, &evm_config_xattrnames, list) {
+ sprintf(temp + offset, "%s\n", xattr->name);
+ offset += strlen(xattr->name) + 1;
+ }
+
+ mutex_unlock(&xattr_list_mutex);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ kfree(temp);
+
+ return rc;
+}
+
+/**
+ * evm_write_xattrs - write() for <securityfs>/evm_xattrs
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int len, err;
+ struct xattr_list *xattr, *tmp;
+ struct audit_buffer *ab;
+ struct iattr newattrs;
+ struct inode *inode;
+
+ if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
+ return -EPERM;
+
+ if (*ppos != 0)
+ return -EINVAL;
+
+ if (count > XATTR_NAME_MAX)
+ return -E2BIG;
+
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR);
+ if (IS_ERR(ab))
+ return PTR_ERR(ab);
+
+ xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
+ if (!xattr) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ xattr->name = memdup_user_nul(buf, count);
+ if (IS_ERR(xattr->name)) {
+ err = PTR_ERR(xattr->name);
+ xattr->name = NULL;
+ goto out;
+ }
+
+ /* Remove any trailing newline */
+ len = strlen(xattr->name);
+ if (len && xattr->name[len-1] == '\n')
+ xattr->name[len-1] = '\0';
+
+ if (strcmp(xattr->name, ".") == 0) {
+ evm_xattrs_locked = 1;
+ newattrs.ia_mode = S_IFREG | 0440;
+ newattrs.ia_valid = ATTR_MODE;
+ inode = evm_xattrs->d_inode;
+ inode_lock(inode);
+ err = simple_setattr(evm_xattrs, &newattrs);
+ inode_unlock(inode);
+ audit_log_format(ab, "locked");
+ if (!err)
+ err = count;
+ goto out;
+ }
+
+ audit_log_format(ab, "xattr=");
+ audit_log_untrustedstring(ab, xattr->name);
+
+ if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
+ XATTR_SECURITY_PREFIX_LEN) != 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Guard against races in evm_read_xattrs */
+ mutex_lock(&xattr_list_mutex);
+ list_for_each_entry(tmp, &evm_config_xattrnames, list) {
+ if (strcmp(xattr->name, tmp->name) == 0) {
+ err = -EEXIST;
+ mutex_unlock(&xattr_list_mutex);
+ goto out;
+ }
+ }
+ list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
+ mutex_unlock(&xattr_list_mutex);
+
+ audit_log_format(ab, " res=0");
+ audit_log_end(ab);
+ return count;
+out:
+ audit_log_format(ab, " res=%d", err);
+ audit_log_end(ab);
+ if (xattr) {
+ kfree(xattr->name);
+ kfree(xattr);
+ }
+ return err;
+}
+
+static const struct file_operations evm_xattr_ops = {
+ .read = evm_read_xattrs,
+ .write = evm_write_xattrs,
+};
+
+static int evm_init_xattrs(void)
+{
+ evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
+ &evm_xattr_ops);
+ if (!evm_xattrs || IS_ERR(evm_xattrs))
+ return -EFAULT;
+
+ return 0;
+}
+#else
+static int evm_init_xattrs(void)
+{
+ return 0;
+}
+#endif
+
int __init evm_init_secfs(void)
{
int error = 0;
- evm_init_tpm = securityfs_create_file("evm", S_IRUSR | S_IRGRP,
- NULL, NULL, &evm_key_ops);
- if (!evm_init_tpm || IS_ERR(evm_init_tpm))
+ evm_dir = securityfs_create_dir("evm", integrity_dir);
+ if (!evm_dir || IS_ERR(evm_dir))
+ return -EFAULT;
+
+ evm_init_tpm = securityfs_create_file("evm", 0660,
+ evm_dir, NULL, &evm_key_ops);
+ if (!evm_init_tpm || IS_ERR(evm_init_tpm)) {
error = -EFAULT;
+ goto out;
+ }
+
+ evm_symlink = securityfs_create_symlink("evm", NULL,
+ "integrity/evm/evm", NULL);
+ if (!evm_symlink || IS_ERR(evm_symlink)) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ if (evm_init_xattrs() != 0) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ return 0;
+out:
+ securityfs_remove(evm_symlink);
+ securityfs_remove(evm_init_tpm);
+ securityfs_remove(evm_dir);
return error;
}