|
@@ -15,14 +15,22 @@
|
|
|
|
|
|
#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
|
|
|
*
|
|
@@ -109,6 +117,166 @@ 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)
|
|
|
+ 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));
|
|
|
+
|
|
|
+ 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 (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);
|
|
|
+ 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;
|
|
@@ -131,6 +299,11 @@ int __init evm_init_secfs(void)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ if (evm_init_xattrs() != 0) {
|
|
|
+ error = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
out:
|
|
|
securityfs_remove(evm_symlink);
|