123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * Copyright (C) 2010 IBM Corporation
- *
- * Authors:
- * Mimi Zohar <zohar@us.ibm.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.
- *
- * File: evm_secfs.c
- * - Used to signal when key is on keyring
- * - Get the key and enable EVM
- */
- #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
- *
- * @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_key(struct file *filp, char __user *buf,
- size_t count, loff_t *ppos)
- {
- char temp[80];
- ssize_t rc;
- if (*ppos != 0)
- return 0;
- sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
- rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
- return rc;
- }
- /**
- * evm_write_key - write() for <securityfs>/evm
- * @file: file pointer, not actually used
- * @buf: where to get the data from
- * @count: bytes sent
- * @ppos: where to start
- *
- * Used to signal that key is on the kernel key ring.
- * - get the integrity hmac key from the kernel key ring
- * - create list of hmac protected extended attributes
- * Returns number of bytes written or error code, as appropriate
- */
- static ssize_t evm_write_key(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
- {
- int i, ret;
- if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
- return -EPERM;
- ret = kstrtoint_from_user(buf, count, 0, &i);
- if (ret)
- return ret;
- /* Reject invalid values */
- if (!i || (i & ~EVM_INIT_MASK) != 0)
- return -EINVAL;
- /* Don't allow a request to freshly enable metadata writes if
- * keys are loaded.
- */
- if ((i & EVM_ALLOW_METADATA_WRITES) &&
- ((evm_initialized & EVM_KEY_MASK) != 0) &&
- !(evm_initialized & EVM_ALLOW_METADATA_WRITES))
- return -EPERM;
- if (i & EVM_INIT_HMAC) {
- ret = evm_init_key();
- if (ret != 0)
- return ret;
- /* Forbid further writes after the symmetric key is loaded */
- i |= EVM_SETUP_COMPLETE;
- }
- evm_initialized |= i;
- /* Don't allow protected metadata modification if a symmetric key
- * is loaded
- */
- if (evm_initialized & EVM_INIT_HMAC)
- evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
- return count;
- }
- static const struct file_operations evm_key_ops = {
- .read = evm_read_key,
- .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 (!ab)
- return -ENOMEM;
- 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_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;
- }
|