1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
266dbc325SMimi Zohar /*
366dbc325SMimi Zohar  * Copyright (C) 2010 IBM Corporation
466dbc325SMimi Zohar  *
566dbc325SMimi Zohar  * Authors:
666dbc325SMimi Zohar  * Mimi Zohar <zohar@us.ibm.com>
766dbc325SMimi Zohar  *
866dbc325SMimi Zohar  * File: evm_secfs.c
966dbc325SMimi Zohar  *	- Used to signal when key is on keyring
1066dbc325SMimi Zohar  *	- Get the key and enable EVM
1166dbc325SMimi Zohar  */
1266dbc325SMimi Zohar 
13fa516b66SMatthew Garrett #include <linux/audit.h>
1466dbc325SMimi Zohar #include <linux/uaccess.h>
15876979c9SPaul Gortmaker #include <linux/init.h>
16fa516b66SMatthew Garrett #include <linux/mutex.h>
1766dbc325SMimi Zohar #include "evm.h"
1866dbc325SMimi Zohar 
190c343af8SMatthew Garrett static struct dentry *evm_dir;
2066dbc325SMimi Zohar static struct dentry *evm_init_tpm;
210c343af8SMatthew Garrett static struct dentry *evm_symlink;
2266dbc325SMimi Zohar 
23fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS
24fa516b66SMatthew Garrett static struct dentry *evm_xattrs;
25fa516b66SMatthew Garrett static DEFINE_MUTEX(xattr_list_mutex);
26fa516b66SMatthew Garrett static int evm_xattrs_locked;
27fa516b66SMatthew Garrett #endif
28fa516b66SMatthew Garrett 
2966dbc325SMimi Zohar /**
3066dbc325SMimi Zohar  * evm_read_key - read() for <securityfs>/evm
3166dbc325SMimi Zohar  *
3266dbc325SMimi Zohar  * @filp: file pointer, not actually used
3366dbc325SMimi Zohar  * @buf: where to put the result
3466dbc325SMimi Zohar  * @count: maximum to send along
3566dbc325SMimi Zohar  * @ppos: where to start
3666dbc325SMimi Zohar  *
3766dbc325SMimi Zohar  * Returns number of bytes read or error code, as appropriate
3866dbc325SMimi Zohar  */
evm_read_key(struct file * filp,char __user * buf,size_t count,loff_t * ppos)3966dbc325SMimi Zohar static ssize_t evm_read_key(struct file *filp, char __user *buf,
4066dbc325SMimi Zohar 			    size_t count, loff_t *ppos)
4166dbc325SMimi Zohar {
4266dbc325SMimi Zohar 	char temp[80];
4366dbc325SMimi Zohar 	ssize_t rc;
4466dbc325SMimi Zohar 
4566dbc325SMimi Zohar 	if (*ppos != 0)
4666dbc325SMimi Zohar 		return 0;
4766dbc325SMimi Zohar 
48ae1ba167SMatthew Garrett 	sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
4966dbc325SMimi Zohar 	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
5066dbc325SMimi Zohar 
5166dbc325SMimi Zohar 	return rc;
5266dbc325SMimi Zohar }
5366dbc325SMimi Zohar 
5466dbc325SMimi Zohar /**
5566dbc325SMimi Zohar  * evm_write_key - write() for <securityfs>/evm
5666dbc325SMimi Zohar  * @file: file pointer, not actually used
5766dbc325SMimi Zohar  * @buf: where to get the data from
5866dbc325SMimi Zohar  * @count: bytes sent
5966dbc325SMimi Zohar  * @ppos: where to start
6066dbc325SMimi Zohar  *
6166dbc325SMimi Zohar  * Used to signal that key is on the kernel key ring.
6266dbc325SMimi Zohar  * - get the integrity hmac key from the kernel key ring
6366dbc325SMimi Zohar  * - create list of hmac protected extended attributes
6466dbc325SMimi Zohar  * Returns number of bytes written or error code, as appropriate
6566dbc325SMimi Zohar  */
evm_write_key(struct file * file,const char __user * buf,size_t count,loff_t * ppos)6666dbc325SMimi Zohar static ssize_t evm_write_key(struct file *file, const char __user *buf,
6766dbc325SMimi Zohar 			     size_t count, loff_t *ppos)
6866dbc325SMimi Zohar {
6949219d9bSMimi Zohar 	unsigned int i;
7049219d9bSMimi Zohar 	int ret;
7166dbc325SMimi Zohar 
72ae1ba167SMatthew Garrett 	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
7366dbc325SMimi Zohar 		return -EPERM;
7466dbc325SMimi Zohar 
7549219d9bSMimi Zohar 	ret = kstrtouint_from_user(buf, count, 0, &i);
76f00d7975SMatthew Garrett 
77f00d7975SMatthew Garrett 	if (ret)
78f00d7975SMatthew Garrett 		return ret;
79f00d7975SMatthew Garrett 
80f00d7975SMatthew Garrett 	/* Reject invalid values */
81f00d7975SMatthew Garrett 	if (!i || (i & ~EVM_INIT_MASK) != 0)
8266dbc325SMimi Zohar 		return -EINVAL;
8366dbc325SMimi Zohar 
849acc89d3SRoberto Sassu 	/*
859acc89d3SRoberto Sassu 	 * Don't allow a request to enable metadata writes if
869acc89d3SRoberto Sassu 	 * an HMAC key is loaded.
87ae1ba167SMatthew Garrett 	 */
88ae1ba167SMatthew Garrett 	if ((i & EVM_ALLOW_METADATA_WRITES) &&
899acc89d3SRoberto Sassu 	    (evm_initialized & EVM_INIT_HMAC) != 0)
90ae1ba167SMatthew Garrett 		return -EPERM;
91ae1ba167SMatthew Garrett 
92f00d7975SMatthew Garrett 	if (i & EVM_INIT_HMAC) {
93f00d7975SMatthew Garrett 		ret = evm_init_key();
94f00d7975SMatthew Garrett 		if (ret != 0)
95f00d7975SMatthew Garrett 			return ret;
96f00d7975SMatthew Garrett 		/* Forbid further writes after the symmetric key is loaded */
97ae1ba167SMatthew Garrett 		i |= EVM_SETUP_COMPLETE;
98f00d7975SMatthew Garrett 	}
9966dbc325SMimi Zohar 
100f00d7975SMatthew Garrett 	evm_initialized |= i;
10176266763SDmitry Kasatkin 
102ae1ba167SMatthew Garrett 	/* Don't allow protected metadata modification if a symmetric key
103ae1ba167SMatthew Garrett 	 * is loaded
104ae1ba167SMatthew Garrett 	 */
105ae1ba167SMatthew Garrett 	if (evm_initialized & EVM_INIT_HMAC)
106ae1ba167SMatthew Garrett 		evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
107ae1ba167SMatthew Garrett 
10866dbc325SMimi Zohar 	return count;
10966dbc325SMimi Zohar }
11066dbc325SMimi Zohar 
11166dbc325SMimi Zohar static const struct file_operations evm_key_ops = {
11266dbc325SMimi Zohar 	.read		= evm_read_key,
11366dbc325SMimi Zohar 	.write		= evm_write_key,
11466dbc325SMimi Zohar };
11566dbc325SMimi Zohar 
116fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS
117fa516b66SMatthew Garrett /**
118fa516b66SMatthew Garrett  * evm_read_xattrs - read() for <securityfs>/evm_xattrs
119fa516b66SMatthew Garrett  *
120fa516b66SMatthew Garrett  * @filp: file pointer, not actually used
121fa516b66SMatthew Garrett  * @buf: where to put the result
122fa516b66SMatthew Garrett  * @count: maximum to send along
123fa516b66SMatthew Garrett  * @ppos: where to start
124fa516b66SMatthew Garrett  *
125fa516b66SMatthew Garrett  * Returns number of bytes read or error code, as appropriate
126fa516b66SMatthew Garrett  */
evm_read_xattrs(struct file * filp,char __user * buf,size_t count,loff_t * ppos)127fa516b66SMatthew Garrett static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
128fa516b66SMatthew Garrett 			       size_t count, loff_t *ppos)
129fa516b66SMatthew Garrett {
130fa516b66SMatthew Garrett 	char *temp;
131fa516b66SMatthew Garrett 	int offset = 0;
132fa516b66SMatthew Garrett 	ssize_t rc, size = 0;
133fa516b66SMatthew Garrett 	struct xattr_list *xattr;
134fa516b66SMatthew Garrett 
135fa516b66SMatthew Garrett 	if (*ppos != 0)
136fa516b66SMatthew Garrett 		return 0;
137fa516b66SMatthew Garrett 
138fa516b66SMatthew Garrett 	rc = mutex_lock_interruptible(&xattr_list_mutex);
139fa516b66SMatthew Garrett 	if (rc)
140fa516b66SMatthew Garrett 		return -ERESTARTSYS;
141fa516b66SMatthew Garrett 
1428c7a703eSRoberto Sassu 	list_for_each_entry(xattr, &evm_config_xattrnames, list) {
1438c7a703eSRoberto Sassu 		if (!xattr->enabled)
1448c7a703eSRoberto Sassu 			continue;
1458c7a703eSRoberto Sassu 
146fa516b66SMatthew Garrett 		size += strlen(xattr->name) + 1;
1478c7a703eSRoberto Sassu 	}
148fa516b66SMatthew Garrett 
149fa516b66SMatthew Garrett 	temp = kmalloc(size + 1, GFP_KERNEL);
150b5c90a75SDan Carpenter 	if (!temp) {
151b5c90a75SDan Carpenter 		mutex_unlock(&xattr_list_mutex);
152fa516b66SMatthew Garrett 		return -ENOMEM;
153b5c90a75SDan Carpenter 	}
154fa516b66SMatthew Garrett 
155fa516b66SMatthew Garrett 	list_for_each_entry(xattr, &evm_config_xattrnames, list) {
1568c7a703eSRoberto Sassu 		if (!xattr->enabled)
1578c7a703eSRoberto Sassu 			continue;
1588c7a703eSRoberto Sassu 
159fa516b66SMatthew Garrett 		sprintf(temp + offset, "%s\n", xattr->name);
160fa516b66SMatthew Garrett 		offset += strlen(xattr->name) + 1;
161fa516b66SMatthew Garrett 	}
162fa516b66SMatthew Garrett 
163fa516b66SMatthew Garrett 	mutex_unlock(&xattr_list_mutex);
164fa516b66SMatthew Garrett 	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
165fa516b66SMatthew Garrett 
166825b8650SColin Ian King 	kfree(temp);
167825b8650SColin Ian King 
168fa516b66SMatthew Garrett 	return rc;
169fa516b66SMatthew Garrett }
170fa516b66SMatthew Garrett 
171fa516b66SMatthew Garrett /**
172fa516b66SMatthew Garrett  * evm_write_xattrs - write() for <securityfs>/evm_xattrs
173fa516b66SMatthew Garrett  * @file: file pointer, not actually used
174fa516b66SMatthew Garrett  * @buf: where to get the data from
175fa516b66SMatthew Garrett  * @count: bytes sent
176fa516b66SMatthew Garrett  * @ppos: where to start
177fa516b66SMatthew Garrett  *
178fa516b66SMatthew Garrett  * Returns number of bytes written or error code, as appropriate
179fa516b66SMatthew Garrett  */
evm_write_xattrs(struct file * file,const char __user * buf,size_t count,loff_t * ppos)180fa516b66SMatthew Garrett static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
181fa516b66SMatthew Garrett 				size_t count, loff_t *ppos)
182fa516b66SMatthew Garrett {
183fa516b66SMatthew Garrett 	int len, err;
184fa516b66SMatthew Garrett 	struct xattr_list *xattr, *tmp;
185fa516b66SMatthew Garrett 	struct audit_buffer *ab;
186fa516b66SMatthew Garrett 	struct iattr newattrs;
187fa516b66SMatthew Garrett 	struct inode *inode;
188fa516b66SMatthew Garrett 
189fa516b66SMatthew Garrett 	if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
190fa516b66SMatthew Garrett 		return -EPERM;
191fa516b66SMatthew Garrett 
192fa516b66SMatthew Garrett 	if (*ppos != 0)
193fa516b66SMatthew Garrett 		return -EINVAL;
194fa516b66SMatthew Garrett 
195fa516b66SMatthew Garrett 	if (count > XATTR_NAME_MAX)
196fa516b66SMatthew Garrett 		return -E2BIG;
197fa516b66SMatthew Garrett 
198a1aa08a0SRichard Guy Briggs 	ab = audit_log_start(audit_context(), GFP_KERNEL,
199a1aa08a0SRichard Guy Briggs 			     AUDIT_INTEGRITY_EVM_XATTR);
200d721c15fSRoberto Sassu 	if (!ab && IS_ENABLED(CONFIG_AUDIT))
2013dd0f18cSWei Yongjun 		return -ENOMEM;
202fa516b66SMatthew Garrett 
203fa516b66SMatthew Garrett 	xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
204fa516b66SMatthew Garrett 	if (!xattr) {
205fa516b66SMatthew Garrett 		err = -ENOMEM;
206fa516b66SMatthew Garrett 		goto out;
207fa516b66SMatthew Garrett 	}
208fa516b66SMatthew Garrett 
2098c7a703eSRoberto Sassu 	xattr->enabled = true;
210fa516b66SMatthew Garrett 	xattr->name = memdup_user_nul(buf, count);
211fa516b66SMatthew Garrett 	if (IS_ERR(xattr->name)) {
212fa516b66SMatthew Garrett 		err = PTR_ERR(xattr->name);
213fa516b66SMatthew Garrett 		xattr->name = NULL;
214fa516b66SMatthew Garrett 		goto out;
215fa516b66SMatthew Garrett 	}
216fa516b66SMatthew Garrett 
217fa516b66SMatthew Garrett 	/* Remove any trailing newline */
218fa516b66SMatthew Garrett 	len = strlen(xattr->name);
219a41d80acSDan Carpenter 	if (len && xattr->name[len-1] == '\n')
220fa516b66SMatthew Garrett 		xattr->name[len-1] = '\0';
221fa516b66SMatthew Garrett 
222a1aa08a0SRichard Guy Briggs 	audit_log_format(ab, "xattr=");
223a1aa08a0SRichard Guy Briggs 	audit_log_untrustedstring(ab, xattr->name);
224a1aa08a0SRichard Guy Briggs 
225fa516b66SMatthew Garrett 	if (strcmp(xattr->name, ".") == 0) {
226fa516b66SMatthew Garrett 		evm_xattrs_locked = 1;
227fa516b66SMatthew Garrett 		newattrs.ia_mode = S_IFREG | 0440;
228fa516b66SMatthew Garrett 		newattrs.ia_valid = ATTR_MODE;
229fa516b66SMatthew Garrett 		inode = evm_xattrs->d_inode;
230fa516b66SMatthew Garrett 		inode_lock(inode);
231*c1632a0fSChristian Brauner 		err = simple_setattr(&nop_mnt_idmap, evm_xattrs, &newattrs);
232fa516b66SMatthew Garrett 		inode_unlock(inode);
233fa516b66SMatthew Garrett 		if (!err)
234fa516b66SMatthew Garrett 			err = count;
235fa516b66SMatthew Garrett 		goto out;
236fa516b66SMatthew Garrett 	}
237fa516b66SMatthew Garrett 
238fa516b66SMatthew Garrett 	if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
239fa516b66SMatthew Garrett 		    XATTR_SECURITY_PREFIX_LEN) != 0) {
240fa516b66SMatthew Garrett 		err = -EINVAL;
241fa516b66SMatthew Garrett 		goto out;
242fa516b66SMatthew Garrett 	}
243fa516b66SMatthew Garrett 
244770f6058SMadhuparna Bhowmik 	/*
245770f6058SMadhuparna Bhowmik 	 * xattr_list_mutex guards against races in evm_read_xattrs().
246770f6058SMadhuparna Bhowmik 	 * Entries are only added to the evm_config_xattrnames list
247770f6058SMadhuparna Bhowmik 	 * and never deleted. Therefore, the list is traversed
248770f6058SMadhuparna Bhowmik 	 * using list_for_each_entry_lockless() without holding
249770f6058SMadhuparna Bhowmik 	 * the mutex in evm_calc_hmac_or_hash(), evm_find_protected_xattrs()
250770f6058SMadhuparna Bhowmik 	 * and evm_protected_xattr().
251770f6058SMadhuparna Bhowmik 	 */
252fa516b66SMatthew Garrett 	mutex_lock(&xattr_list_mutex);
253fa516b66SMatthew Garrett 	list_for_each_entry(tmp, &evm_config_xattrnames, list) {
254fa516b66SMatthew Garrett 		if (strcmp(xattr->name, tmp->name) == 0) {
255fa516b66SMatthew Garrett 			err = -EEXIST;
2568c7a703eSRoberto Sassu 			if (!tmp->enabled) {
2578c7a703eSRoberto Sassu 				tmp->enabled = true;
2588c7a703eSRoberto Sassu 				err = count;
2598c7a703eSRoberto Sassu 			}
260fa516b66SMatthew Garrett 			mutex_unlock(&xattr_list_mutex);
261fa516b66SMatthew Garrett 			goto out;
262fa516b66SMatthew Garrett 		}
263fa516b66SMatthew Garrett 	}
264fa516b66SMatthew Garrett 	list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
265fa516b66SMatthew Garrett 	mutex_unlock(&xattr_list_mutex);
266fa516b66SMatthew Garrett 
267fa516b66SMatthew Garrett 	audit_log_format(ab, " res=0");
268fa516b66SMatthew Garrett 	audit_log_end(ab);
269fa516b66SMatthew Garrett 	return count;
270fa516b66SMatthew Garrett out:
2718c7a703eSRoberto Sassu 	audit_log_format(ab, " res=%d", (err < 0) ? err : 0);
272fa516b66SMatthew Garrett 	audit_log_end(ab);
27372acd64dSColin Ian King 	if (xattr) {
274fa516b66SMatthew Garrett 		kfree(xattr->name);
275fa516b66SMatthew Garrett 		kfree(xattr);
27672acd64dSColin Ian King 	}
277fa516b66SMatthew Garrett 	return err;
278fa516b66SMatthew Garrett }
279fa516b66SMatthew Garrett 
280fa516b66SMatthew Garrett static const struct file_operations evm_xattr_ops = {
281fa516b66SMatthew Garrett 	.read		= evm_read_xattrs,
282fa516b66SMatthew Garrett 	.write		= evm_write_xattrs,
283fa516b66SMatthew Garrett };
284fa516b66SMatthew Garrett 
evm_init_xattrs(void)285fa516b66SMatthew Garrett static int evm_init_xattrs(void)
286fa516b66SMatthew Garrett {
287fa516b66SMatthew Garrett 	evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
288fa516b66SMatthew Garrett 					    &evm_xattr_ops);
289fa516b66SMatthew Garrett 	if (!evm_xattrs || IS_ERR(evm_xattrs))
290fa516b66SMatthew Garrett 		return -EFAULT;
291fa516b66SMatthew Garrett 
292fa516b66SMatthew Garrett 	return 0;
293fa516b66SMatthew Garrett }
294fa516b66SMatthew Garrett #else
evm_init_xattrs(void)295fa516b66SMatthew Garrett static int evm_init_xattrs(void)
296fa516b66SMatthew Garrett {
297fa516b66SMatthew Garrett 	return 0;
298fa516b66SMatthew Garrett }
299fa516b66SMatthew Garrett #endif
300fa516b66SMatthew Garrett 
evm_init_secfs(void)30166dbc325SMimi Zohar int __init evm_init_secfs(void)
30266dbc325SMimi Zohar {
30366dbc325SMimi Zohar 	int error = 0;
30466dbc325SMimi Zohar 
3050c343af8SMatthew Garrett 	evm_dir = securityfs_create_dir("evm", integrity_dir);
3060c343af8SMatthew Garrett 	if (!evm_dir || IS_ERR(evm_dir))
3070c343af8SMatthew Garrett 		return -EFAULT;
3080c343af8SMatthew Garrett 
3090c343af8SMatthew Garrett 	evm_init_tpm = securityfs_create_file("evm", 0660,
3100c343af8SMatthew Garrett 					      evm_dir, NULL, &evm_key_ops);
3110c343af8SMatthew Garrett 	if (!evm_init_tpm || IS_ERR(evm_init_tpm)) {
31266dbc325SMimi Zohar 		error = -EFAULT;
3130c343af8SMatthew Garrett 		goto out;
3140c343af8SMatthew Garrett 	}
3150c343af8SMatthew Garrett 
3160c343af8SMatthew Garrett 	evm_symlink = securityfs_create_symlink("evm", NULL,
3170c343af8SMatthew Garrett 						"integrity/evm/evm", NULL);
3180c343af8SMatthew Garrett 	if (!evm_symlink || IS_ERR(evm_symlink)) {
3190c343af8SMatthew Garrett 		error = -EFAULT;
3200c343af8SMatthew Garrett 		goto out;
3210c343af8SMatthew Garrett 	}
3220c343af8SMatthew Garrett 
323fa516b66SMatthew Garrett 	if (evm_init_xattrs() != 0) {
324fa516b66SMatthew Garrett 		error = -EFAULT;
325fa516b66SMatthew Garrett 		goto out;
326fa516b66SMatthew Garrett 	}
327fa516b66SMatthew Garrett 
3280c343af8SMatthew Garrett 	return 0;
3290c343af8SMatthew Garrett out:
3300c343af8SMatthew Garrett 	securityfs_remove(evm_symlink);
3310c343af8SMatthew Garrett 	securityfs_remove(evm_init_tpm);
3320c343af8SMatthew Garrett 	securityfs_remove(evm_dir);
33366dbc325SMimi Zohar 	return error;
33466dbc325SMimi Zohar }
335