18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010 IBM Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci * Mimi Zohar <zohar@us.ibm.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * File: evm_secfs.c
98c2ecf20Sopenharmony_ci *	- Used to signal when key is on keyring
108c2ecf20Sopenharmony_ci *	- Get the key and enable EVM
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/audit.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include "evm.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic struct dentry *evm_dir;
208c2ecf20Sopenharmony_cistatic struct dentry *evm_init_tpm;
218c2ecf20Sopenharmony_cistatic struct dentry *evm_symlink;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#ifdef CONFIG_EVM_ADD_XATTRS
248c2ecf20Sopenharmony_cistatic struct dentry *evm_xattrs;
258c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(xattr_list_mutex);
268c2ecf20Sopenharmony_cistatic int evm_xattrs_locked;
278c2ecf20Sopenharmony_ci#endif
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/**
308c2ecf20Sopenharmony_ci * evm_read_key - read() for <securityfs>/evm
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * @filp: file pointer, not actually used
338c2ecf20Sopenharmony_ci * @buf: where to put the result
348c2ecf20Sopenharmony_ci * @count: maximum to send along
358c2ecf20Sopenharmony_ci * @ppos: where to start
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic ssize_t evm_read_key(struct file *filp, char __user *buf,
408c2ecf20Sopenharmony_ci			    size_t count, loff_t *ppos)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	char temp[80];
438c2ecf20Sopenharmony_ci	ssize_t rc;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (*ppos != 0)
468c2ecf20Sopenharmony_ci		return 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
498c2ecf20Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return rc;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/**
558c2ecf20Sopenharmony_ci * evm_write_key - write() for <securityfs>/evm
568c2ecf20Sopenharmony_ci * @file: file pointer, not actually used
578c2ecf20Sopenharmony_ci * @buf: where to get the data from
588c2ecf20Sopenharmony_ci * @count: bytes sent
598c2ecf20Sopenharmony_ci * @ppos: where to start
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * Used to signal that key is on the kernel key ring.
628c2ecf20Sopenharmony_ci * - get the integrity hmac key from the kernel key ring
638c2ecf20Sopenharmony_ci * - create list of hmac protected extended attributes
648c2ecf20Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic ssize_t evm_write_key(struct file *file, const char __user *buf,
678c2ecf20Sopenharmony_ci			     size_t count, loff_t *ppos)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	unsigned int i;
708c2ecf20Sopenharmony_ci	int ret;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
738c2ecf20Sopenharmony_ci		return -EPERM;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	ret = kstrtouint_from_user(buf, count, 0, &i);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (ret)
788c2ecf20Sopenharmony_ci		return ret;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Reject invalid values */
818c2ecf20Sopenharmony_ci	if (!i || (i & ~EVM_INIT_MASK) != 0)
828c2ecf20Sopenharmony_ci		return -EINVAL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/*
858c2ecf20Sopenharmony_ci	 * Don't allow a request to enable metadata writes if
868c2ecf20Sopenharmony_ci	 * an HMAC key is loaded.
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	if ((i & EVM_ALLOW_METADATA_WRITES) &&
898c2ecf20Sopenharmony_ci	    (evm_initialized & EVM_INIT_HMAC) != 0)
908c2ecf20Sopenharmony_ci		return -EPERM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (i & EVM_INIT_HMAC) {
938c2ecf20Sopenharmony_ci		ret = evm_init_key();
948c2ecf20Sopenharmony_ci		if (ret != 0)
958c2ecf20Sopenharmony_ci			return ret;
968c2ecf20Sopenharmony_ci		/* Forbid further writes after the symmetric key is loaded */
978c2ecf20Sopenharmony_ci		i |= EVM_SETUP_COMPLETE;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	evm_initialized |= i;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Don't allow protected metadata modification if a symmetric key
1038c2ecf20Sopenharmony_ci	 * is loaded
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	if (evm_initialized & EVM_INIT_HMAC)
1068c2ecf20Sopenharmony_ci		evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return count;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const struct file_operations evm_key_ops = {
1128c2ecf20Sopenharmony_ci	.read		= evm_read_key,
1138c2ecf20Sopenharmony_ci	.write		= evm_write_key,
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#ifdef CONFIG_EVM_ADD_XATTRS
1178c2ecf20Sopenharmony_ci/**
1188c2ecf20Sopenharmony_ci * evm_read_xattrs - read() for <securityfs>/evm_xattrs
1198c2ecf20Sopenharmony_ci *
1208c2ecf20Sopenharmony_ci * @filp: file pointer, not actually used
1218c2ecf20Sopenharmony_ci * @buf: where to put the result
1228c2ecf20Sopenharmony_ci * @count: maximum to send along
1238c2ecf20Sopenharmony_ci * @ppos: where to start
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * Returns number of bytes read or error code, as appropriate
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_cistatic ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
1288c2ecf20Sopenharmony_ci			       size_t count, loff_t *ppos)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	char *temp;
1318c2ecf20Sopenharmony_ci	int offset = 0;
1328c2ecf20Sopenharmony_ci	ssize_t rc, size = 0;
1338c2ecf20Sopenharmony_ci	struct xattr_list *xattr;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (*ppos != 0)
1368c2ecf20Sopenharmony_ci		return 0;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	rc = mutex_lock_interruptible(&xattr_list_mutex);
1398c2ecf20Sopenharmony_ci	if (rc)
1408c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	list_for_each_entry(xattr, &evm_config_xattrnames, list)
1438c2ecf20Sopenharmony_ci		size += strlen(xattr->name) + 1;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	temp = kmalloc(size + 1, GFP_KERNEL);
1468c2ecf20Sopenharmony_ci	if (!temp) {
1478c2ecf20Sopenharmony_ci		mutex_unlock(&xattr_list_mutex);
1488c2ecf20Sopenharmony_ci		return -ENOMEM;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	list_for_each_entry(xattr, &evm_config_xattrnames, list) {
1528c2ecf20Sopenharmony_ci		sprintf(temp + offset, "%s\n", xattr->name);
1538c2ecf20Sopenharmony_ci		offset += strlen(xattr->name) + 1;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	mutex_unlock(&xattr_list_mutex);
1578c2ecf20Sopenharmony_ci	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	kfree(temp);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return rc;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/**
1658c2ecf20Sopenharmony_ci * evm_write_xattrs - write() for <securityfs>/evm_xattrs
1668c2ecf20Sopenharmony_ci * @file: file pointer, not actually used
1678c2ecf20Sopenharmony_ci * @buf: where to get the data from
1688c2ecf20Sopenharmony_ci * @count: bytes sent
1698c2ecf20Sopenharmony_ci * @ppos: where to start
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * Returns number of bytes written or error code, as appropriate
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_cistatic ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
1748c2ecf20Sopenharmony_ci				size_t count, loff_t *ppos)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	int len, err;
1778c2ecf20Sopenharmony_ci	struct xattr_list *xattr, *tmp;
1788c2ecf20Sopenharmony_ci	struct audit_buffer *ab;
1798c2ecf20Sopenharmony_ci	struct iattr newattrs;
1808c2ecf20Sopenharmony_ci	struct inode *inode;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
1838c2ecf20Sopenharmony_ci		return -EPERM;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (*ppos != 0)
1868c2ecf20Sopenharmony_ci		return -EINVAL;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (count > XATTR_NAME_MAX)
1898c2ecf20Sopenharmony_ci		return -E2BIG;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ab = audit_log_start(audit_context(), GFP_KERNEL,
1928c2ecf20Sopenharmony_ci			     AUDIT_INTEGRITY_EVM_XATTR);
1938c2ecf20Sopenharmony_ci	if (!ab)
1948c2ecf20Sopenharmony_ci		return -ENOMEM;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
1978c2ecf20Sopenharmony_ci	if (!xattr) {
1988c2ecf20Sopenharmony_ci		err = -ENOMEM;
1998c2ecf20Sopenharmony_ci		goto out;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	xattr->name = memdup_user_nul(buf, count);
2038c2ecf20Sopenharmony_ci	if (IS_ERR(xattr->name)) {
2048c2ecf20Sopenharmony_ci		err = PTR_ERR(xattr->name);
2058c2ecf20Sopenharmony_ci		xattr->name = NULL;
2068c2ecf20Sopenharmony_ci		goto out;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Remove any trailing newline */
2108c2ecf20Sopenharmony_ci	len = strlen(xattr->name);
2118c2ecf20Sopenharmony_ci	if (len && xattr->name[len-1] == '\n')
2128c2ecf20Sopenharmony_ci		xattr->name[len-1] = '\0';
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	audit_log_format(ab, "xattr=");
2158c2ecf20Sopenharmony_ci	audit_log_untrustedstring(ab, xattr->name);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (strcmp(xattr->name, ".") == 0) {
2188c2ecf20Sopenharmony_ci		evm_xattrs_locked = 1;
2198c2ecf20Sopenharmony_ci		newattrs.ia_mode = S_IFREG | 0440;
2208c2ecf20Sopenharmony_ci		newattrs.ia_valid = ATTR_MODE;
2218c2ecf20Sopenharmony_ci		inode = evm_xattrs->d_inode;
2228c2ecf20Sopenharmony_ci		inode_lock(inode);
2238c2ecf20Sopenharmony_ci		err = simple_setattr(evm_xattrs, &newattrs);
2248c2ecf20Sopenharmony_ci		inode_unlock(inode);
2258c2ecf20Sopenharmony_ci		if (!err)
2268c2ecf20Sopenharmony_ci			err = count;
2278c2ecf20Sopenharmony_ci		goto out;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
2318c2ecf20Sopenharmony_ci		    XATTR_SECURITY_PREFIX_LEN) != 0) {
2328c2ecf20Sopenharmony_ci		err = -EINVAL;
2338c2ecf20Sopenharmony_ci		goto out;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * xattr_list_mutex guards against races in evm_read_xattrs().
2388c2ecf20Sopenharmony_ci	 * Entries are only added to the evm_config_xattrnames list
2398c2ecf20Sopenharmony_ci	 * and never deleted. Therefore, the list is traversed
2408c2ecf20Sopenharmony_ci	 * using list_for_each_entry_lockless() without holding
2418c2ecf20Sopenharmony_ci	 * the mutex in evm_calc_hmac_or_hash(), evm_find_protected_xattrs()
2428c2ecf20Sopenharmony_ci	 * and evm_protected_xattr().
2438c2ecf20Sopenharmony_ci	 */
2448c2ecf20Sopenharmony_ci	mutex_lock(&xattr_list_mutex);
2458c2ecf20Sopenharmony_ci	list_for_each_entry(tmp, &evm_config_xattrnames, list) {
2468c2ecf20Sopenharmony_ci		if (strcmp(xattr->name, tmp->name) == 0) {
2478c2ecf20Sopenharmony_ci			err = -EEXIST;
2488c2ecf20Sopenharmony_ci			mutex_unlock(&xattr_list_mutex);
2498c2ecf20Sopenharmony_ci			goto out;
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci	list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
2538c2ecf20Sopenharmony_ci	mutex_unlock(&xattr_list_mutex);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	audit_log_format(ab, " res=0");
2568c2ecf20Sopenharmony_ci	audit_log_end(ab);
2578c2ecf20Sopenharmony_ci	return count;
2588c2ecf20Sopenharmony_ciout:
2598c2ecf20Sopenharmony_ci	audit_log_format(ab, " res=%d", err);
2608c2ecf20Sopenharmony_ci	audit_log_end(ab);
2618c2ecf20Sopenharmony_ci	if (xattr) {
2628c2ecf20Sopenharmony_ci		kfree(xattr->name);
2638c2ecf20Sopenharmony_ci		kfree(xattr);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	return err;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic const struct file_operations evm_xattr_ops = {
2698c2ecf20Sopenharmony_ci	.read		= evm_read_xattrs,
2708c2ecf20Sopenharmony_ci	.write		= evm_write_xattrs,
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int evm_init_xattrs(void)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
2768c2ecf20Sopenharmony_ci					    &evm_xattr_ops);
2778c2ecf20Sopenharmony_ci	if (!evm_xattrs || IS_ERR(evm_xattrs))
2788c2ecf20Sopenharmony_ci		return -EFAULT;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci#else
2838c2ecf20Sopenharmony_cistatic int evm_init_xattrs(void)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci#endif
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ciint __init evm_init_secfs(void)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	int error = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	evm_dir = securityfs_create_dir("evm", integrity_dir);
2948c2ecf20Sopenharmony_ci	if (!evm_dir || IS_ERR(evm_dir))
2958c2ecf20Sopenharmony_ci		return -EFAULT;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	evm_init_tpm = securityfs_create_file("evm", 0660,
2988c2ecf20Sopenharmony_ci					      evm_dir, NULL, &evm_key_ops);
2998c2ecf20Sopenharmony_ci	if (!evm_init_tpm || IS_ERR(evm_init_tpm)) {
3008c2ecf20Sopenharmony_ci		error = -EFAULT;
3018c2ecf20Sopenharmony_ci		goto out;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	evm_symlink = securityfs_create_symlink("evm", NULL,
3058c2ecf20Sopenharmony_ci						"integrity/evm/evm", NULL);
3068c2ecf20Sopenharmony_ci	if (!evm_symlink || IS_ERR(evm_symlink)) {
3078c2ecf20Sopenharmony_ci		error = -EFAULT;
3088c2ecf20Sopenharmony_ci		goto out;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (evm_init_xattrs() != 0) {
3128c2ecf20Sopenharmony_ci		error = -EFAULT;
3138c2ecf20Sopenharmony_ci		goto out;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ciout:
3188c2ecf20Sopenharmony_ci	securityfs_remove(evm_symlink);
3198c2ecf20Sopenharmony_ci	securityfs_remove(evm_init_tpm);
3208c2ecf20Sopenharmony_ci	securityfs_remove(evm_dir);
3218c2ecf20Sopenharmony_ci	return error;
3228c2ecf20Sopenharmony_ci}
323