166dbc325SMimi Zohar /*
266dbc325SMimi Zohar  * Copyright (C) 2010 IBM Corporation
366dbc325SMimi Zohar  *
466dbc325SMimi Zohar  * Authors:
566dbc325SMimi Zohar  * Mimi Zohar <zohar@us.ibm.com>
666dbc325SMimi Zohar  *
766dbc325SMimi Zohar  * This program is free software; you can redistribute it and/or modify
866dbc325SMimi Zohar  * it under the terms of the GNU General Public License as published by
966dbc325SMimi Zohar  * the Free Software Foundation, version 2 of the License.
1066dbc325SMimi Zohar  *
1166dbc325SMimi Zohar  * File: evm_secfs.c
1266dbc325SMimi Zohar  *	- Used to signal when key is on keyring
1366dbc325SMimi Zohar  *	- Get the key and enable EVM
1466dbc325SMimi Zohar  */
1566dbc325SMimi Zohar 
1620ee451fSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1720ee451fSJoe Perches 
18fa516b66SMatthew Garrett #include <linux/audit.h>
1966dbc325SMimi Zohar #include <linux/uaccess.h>
2066dbc325SMimi Zohar #include <linux/module.h>
21fa516b66SMatthew Garrett #include <linux/mutex.h>
2266dbc325SMimi Zohar #include "evm.h"
2366dbc325SMimi Zohar 
240c343af8SMatthew Garrett static struct dentry *evm_dir;
2566dbc325SMimi Zohar static struct dentry *evm_init_tpm;
260c343af8SMatthew Garrett static struct dentry *evm_symlink;
2766dbc325SMimi Zohar 
28fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS
29fa516b66SMatthew Garrett static struct dentry *evm_xattrs;
30fa516b66SMatthew Garrett static DEFINE_MUTEX(xattr_list_mutex);
31fa516b66SMatthew Garrett static int evm_xattrs_locked;
32fa516b66SMatthew Garrett #endif
33fa516b66SMatthew Garrett 
3466dbc325SMimi Zohar /**
3566dbc325SMimi Zohar  * evm_read_key - read() for <securityfs>/evm
3666dbc325SMimi Zohar  *
3766dbc325SMimi Zohar  * @filp: file pointer, not actually used
3866dbc325SMimi Zohar  * @buf: where to put the result
3966dbc325SMimi Zohar  * @count: maximum to send along
4066dbc325SMimi Zohar  * @ppos: where to start
4166dbc325SMimi Zohar  *
4266dbc325SMimi Zohar  * Returns number of bytes read or error code, as appropriate
4366dbc325SMimi Zohar  */
4466dbc325SMimi Zohar static ssize_t evm_read_key(struct file *filp, char __user *buf,
4566dbc325SMimi Zohar 			    size_t count, loff_t *ppos)
4666dbc325SMimi Zohar {
4766dbc325SMimi Zohar 	char temp[80];
4866dbc325SMimi Zohar 	ssize_t rc;
4966dbc325SMimi Zohar 
5066dbc325SMimi Zohar 	if (*ppos != 0)
5166dbc325SMimi Zohar 		return 0;
5266dbc325SMimi Zohar 
53ae1ba167SMatthew Garrett 	sprintf(temp, "%d", (evm_initialized & ~EVM_SETUP_COMPLETE));
5466dbc325SMimi Zohar 	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
5566dbc325SMimi Zohar 
5666dbc325SMimi Zohar 	return rc;
5766dbc325SMimi Zohar }
5866dbc325SMimi Zohar 
5966dbc325SMimi Zohar /**
6066dbc325SMimi Zohar  * evm_write_key - write() for <securityfs>/evm
6166dbc325SMimi Zohar  * @file: file pointer, not actually used
6266dbc325SMimi Zohar  * @buf: where to get the data from
6366dbc325SMimi Zohar  * @count: bytes sent
6466dbc325SMimi Zohar  * @ppos: where to start
6566dbc325SMimi Zohar  *
6666dbc325SMimi Zohar  * Used to signal that key is on the kernel key ring.
6766dbc325SMimi Zohar  * - get the integrity hmac key from the kernel key ring
6866dbc325SMimi Zohar  * - create list of hmac protected extended attributes
6966dbc325SMimi Zohar  * Returns number of bytes written or error code, as appropriate
7066dbc325SMimi Zohar  */
7166dbc325SMimi Zohar static ssize_t evm_write_key(struct file *file, const char __user *buf,
7266dbc325SMimi Zohar 			     size_t count, loff_t *ppos)
7366dbc325SMimi Zohar {
74f00d7975SMatthew Garrett 	int i, ret;
7566dbc325SMimi Zohar 
76ae1ba167SMatthew Garrett 	if (!capable(CAP_SYS_ADMIN) || (evm_initialized & EVM_SETUP_COMPLETE))
7766dbc325SMimi Zohar 		return -EPERM;
7866dbc325SMimi Zohar 
79f00d7975SMatthew Garrett 	ret = kstrtoint_from_user(buf, count, 0, &i);
80f00d7975SMatthew Garrett 
81f00d7975SMatthew Garrett 	if (ret)
82f00d7975SMatthew Garrett 		return ret;
83f00d7975SMatthew Garrett 
84f00d7975SMatthew Garrett 	/* Reject invalid values */
85f00d7975SMatthew Garrett 	if (!i || (i & ~EVM_INIT_MASK) != 0)
8666dbc325SMimi Zohar 		return -EINVAL;
8766dbc325SMimi Zohar 
88ae1ba167SMatthew Garrett 	/* Don't allow a request to freshly enable metadata writes if
89ae1ba167SMatthew Garrett 	 * keys are loaded.
90ae1ba167SMatthew Garrett 	 */
91ae1ba167SMatthew Garrett 	if ((i & EVM_ALLOW_METADATA_WRITES) &&
92ae1ba167SMatthew Garrett 	    ((evm_initialized & EVM_KEY_MASK) != 0) &&
93ae1ba167SMatthew Garrett 	    !(evm_initialized & EVM_ALLOW_METADATA_WRITES))
94ae1ba167SMatthew Garrett 		return -EPERM;
95ae1ba167SMatthew Garrett 
96f00d7975SMatthew Garrett 	if (i & EVM_INIT_HMAC) {
97f00d7975SMatthew Garrett 		ret = evm_init_key();
98f00d7975SMatthew Garrett 		if (ret != 0)
99f00d7975SMatthew Garrett 			return ret;
100f00d7975SMatthew Garrett 		/* Forbid further writes after the symmetric key is loaded */
101ae1ba167SMatthew Garrett 		i |= EVM_SETUP_COMPLETE;
102f00d7975SMatthew Garrett 	}
10366dbc325SMimi Zohar 
104f00d7975SMatthew Garrett 	evm_initialized |= i;
10576266763SDmitry Kasatkin 
106ae1ba167SMatthew Garrett 	/* Don't allow protected metadata modification if a symmetric key
107ae1ba167SMatthew Garrett 	 * is loaded
108ae1ba167SMatthew Garrett 	 */
109ae1ba167SMatthew Garrett 	if (evm_initialized & EVM_INIT_HMAC)
110ae1ba167SMatthew Garrett 		evm_initialized &= ~(EVM_ALLOW_METADATA_WRITES);
111ae1ba167SMatthew Garrett 
11266dbc325SMimi Zohar 	return count;
11366dbc325SMimi Zohar }
11466dbc325SMimi Zohar 
11566dbc325SMimi Zohar static const struct file_operations evm_key_ops = {
11666dbc325SMimi Zohar 	.read		= evm_read_key,
11766dbc325SMimi Zohar 	.write		= evm_write_key,
11866dbc325SMimi Zohar };
11966dbc325SMimi Zohar 
120fa516b66SMatthew Garrett #ifdef CONFIG_EVM_ADD_XATTRS
121fa516b66SMatthew Garrett /**
122fa516b66SMatthew Garrett  * evm_read_xattrs - read() for <securityfs>/evm_xattrs
123fa516b66SMatthew Garrett  *
124fa516b66SMatthew Garrett  * @filp: file pointer, not actually used
125fa516b66SMatthew Garrett  * @buf: where to put the result
126fa516b66SMatthew Garrett  * @count: maximum to send along
127fa516b66SMatthew Garrett  * @ppos: where to start
128fa516b66SMatthew Garrett  *
129fa516b66SMatthew Garrett  * Returns number of bytes read or error code, as appropriate
130fa516b66SMatthew Garrett  */
131fa516b66SMatthew Garrett static ssize_t evm_read_xattrs(struct file *filp, char __user *buf,
132fa516b66SMatthew Garrett 			       size_t count, loff_t *ppos)
133fa516b66SMatthew Garrett {
134fa516b66SMatthew Garrett 	char *temp;
135fa516b66SMatthew Garrett 	int offset = 0;
136fa516b66SMatthew Garrett 	ssize_t rc, size = 0;
137fa516b66SMatthew Garrett 	struct xattr_list *xattr;
138fa516b66SMatthew Garrett 
139fa516b66SMatthew Garrett 	if (*ppos != 0)
140fa516b66SMatthew Garrett 		return 0;
141fa516b66SMatthew Garrett 
142fa516b66SMatthew Garrett 	rc = mutex_lock_interruptible(&xattr_list_mutex);
143fa516b66SMatthew Garrett 	if (rc)
144fa516b66SMatthew Garrett 		return -ERESTARTSYS;
145fa516b66SMatthew Garrett 
146fa516b66SMatthew Garrett 	list_for_each_entry(xattr, &evm_config_xattrnames, list)
147fa516b66SMatthew Garrett 		size += strlen(xattr->name) + 1;
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) {
156fa516b66SMatthew Garrett 		sprintf(temp + offset, "%s\n", xattr->name);
157fa516b66SMatthew Garrett 		offset += strlen(xattr->name) + 1;
158fa516b66SMatthew Garrett 	}
159fa516b66SMatthew Garrett 
160fa516b66SMatthew Garrett 	mutex_unlock(&xattr_list_mutex);
161fa516b66SMatthew Garrett 	rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
162fa516b66SMatthew Garrett 
163825b8650SColin Ian King 	kfree(temp);
164825b8650SColin Ian King 
165fa516b66SMatthew Garrett 	return rc;
166fa516b66SMatthew Garrett }
167fa516b66SMatthew Garrett 
168fa516b66SMatthew Garrett /**
169fa516b66SMatthew Garrett  * evm_write_xattrs - write() for <securityfs>/evm_xattrs
170fa516b66SMatthew Garrett  * @file: file pointer, not actually used
171fa516b66SMatthew Garrett  * @buf: where to get the data from
172fa516b66SMatthew Garrett  * @count: bytes sent
173fa516b66SMatthew Garrett  * @ppos: where to start
174fa516b66SMatthew Garrett  *
175fa516b66SMatthew Garrett  * Returns number of bytes written or error code, as appropriate
176fa516b66SMatthew Garrett  */
177fa516b66SMatthew Garrett static ssize_t evm_write_xattrs(struct file *file, const char __user *buf,
178fa516b66SMatthew Garrett 				size_t count, loff_t *ppos)
179fa516b66SMatthew Garrett {
180fa516b66SMatthew Garrett 	int len, err;
181fa516b66SMatthew Garrett 	struct xattr_list *xattr, *tmp;
182fa516b66SMatthew Garrett 	struct audit_buffer *ab;
183fa516b66SMatthew Garrett 	struct iattr newattrs;
184fa516b66SMatthew Garrett 	struct inode *inode;
185fa516b66SMatthew Garrett 
186fa516b66SMatthew Garrett 	if (!capable(CAP_SYS_ADMIN) || evm_xattrs_locked)
187fa516b66SMatthew Garrett 		return -EPERM;
188fa516b66SMatthew Garrett 
189fa516b66SMatthew Garrett 	if (*ppos != 0)
190fa516b66SMatthew Garrett 		return -EINVAL;
191fa516b66SMatthew Garrett 
192fa516b66SMatthew Garrett 	if (count > XATTR_NAME_MAX)
193fa516b66SMatthew Garrett 		return -E2BIG;
194fa516b66SMatthew Garrett 
195fa516b66SMatthew Garrett 	ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR);
1963dd0f18cSWei Yongjun 	if (!ab)
1973dd0f18cSWei Yongjun 		return -ENOMEM;
198fa516b66SMatthew Garrett 
199fa516b66SMatthew Garrett 	xattr = kmalloc(sizeof(struct xattr_list), GFP_KERNEL);
200fa516b66SMatthew Garrett 	if (!xattr) {
201fa516b66SMatthew Garrett 		err = -ENOMEM;
202fa516b66SMatthew Garrett 		goto out;
203fa516b66SMatthew Garrett 	}
204fa516b66SMatthew Garrett 
205fa516b66SMatthew Garrett 	xattr->name = memdup_user_nul(buf, count);
206fa516b66SMatthew Garrett 	if (IS_ERR(xattr->name)) {
207fa516b66SMatthew Garrett 		err = PTR_ERR(xattr->name);
208fa516b66SMatthew Garrett 		xattr->name = NULL;
209fa516b66SMatthew Garrett 		goto out;
210fa516b66SMatthew Garrett 	}
211fa516b66SMatthew Garrett 
212fa516b66SMatthew Garrett 	/* Remove any trailing newline */
213fa516b66SMatthew Garrett 	len = strlen(xattr->name);
214a41d80acSDan Carpenter 	if (len && xattr->name[len-1] == '\n')
215fa516b66SMatthew Garrett 		xattr->name[len-1] = '\0';
216fa516b66SMatthew Garrett 
217fa516b66SMatthew Garrett 	if (strcmp(xattr->name, ".") == 0) {
218fa516b66SMatthew Garrett 		evm_xattrs_locked = 1;
219fa516b66SMatthew Garrett 		newattrs.ia_mode = S_IFREG | 0440;
220fa516b66SMatthew Garrett 		newattrs.ia_valid = ATTR_MODE;
221fa516b66SMatthew Garrett 		inode = evm_xattrs->d_inode;
222fa516b66SMatthew Garrett 		inode_lock(inode);
223fa516b66SMatthew Garrett 		err = simple_setattr(evm_xattrs, &newattrs);
224fa516b66SMatthew Garrett 		inode_unlock(inode);
225fa516b66SMatthew Garrett 		audit_log_format(ab, "locked");
226fa516b66SMatthew Garrett 		if (!err)
227fa516b66SMatthew Garrett 			err = count;
228fa516b66SMatthew Garrett 		goto out;
229fa516b66SMatthew Garrett 	}
230fa516b66SMatthew Garrett 
231fa516b66SMatthew Garrett 	audit_log_format(ab, "xattr=");
232fa516b66SMatthew Garrett 	audit_log_untrustedstring(ab, xattr->name);
233fa516b66SMatthew Garrett 
234fa516b66SMatthew Garrett 	if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
235fa516b66SMatthew Garrett 		    XATTR_SECURITY_PREFIX_LEN) != 0) {
236fa516b66SMatthew Garrett 		err = -EINVAL;
237fa516b66SMatthew Garrett 		goto out;
238fa516b66SMatthew Garrett 	}
239fa516b66SMatthew Garrett 
240fa516b66SMatthew Garrett 	/* Guard against races in evm_read_xattrs */
241fa516b66SMatthew Garrett 	mutex_lock(&xattr_list_mutex);
242fa516b66SMatthew Garrett 	list_for_each_entry(tmp, &evm_config_xattrnames, list) {
243fa516b66SMatthew Garrett 		if (strcmp(xattr->name, tmp->name) == 0) {
244fa516b66SMatthew Garrett 			err = -EEXIST;
245fa516b66SMatthew Garrett 			mutex_unlock(&xattr_list_mutex);
246fa516b66SMatthew Garrett 			goto out;
247fa516b66SMatthew Garrett 		}
248fa516b66SMatthew Garrett 	}
249fa516b66SMatthew Garrett 	list_add_tail_rcu(&xattr->list, &evm_config_xattrnames);
250fa516b66SMatthew Garrett 	mutex_unlock(&xattr_list_mutex);
251fa516b66SMatthew Garrett 
252fa516b66SMatthew Garrett 	audit_log_format(ab, " res=0");
253fa516b66SMatthew Garrett 	audit_log_end(ab);
254fa516b66SMatthew Garrett 	return count;
255fa516b66SMatthew Garrett out:
256fa516b66SMatthew Garrett 	audit_log_format(ab, " res=%d", err);
257fa516b66SMatthew Garrett 	audit_log_end(ab);
25872acd64dSColin Ian King 	if (xattr) {
259fa516b66SMatthew Garrett 		kfree(xattr->name);
260fa516b66SMatthew Garrett 		kfree(xattr);
26172acd64dSColin Ian King 	}
262fa516b66SMatthew Garrett 	return err;
263fa516b66SMatthew Garrett }
264fa516b66SMatthew Garrett 
265fa516b66SMatthew Garrett static const struct file_operations evm_xattr_ops = {
266fa516b66SMatthew Garrett 	.read		= evm_read_xattrs,
267fa516b66SMatthew Garrett 	.write		= evm_write_xattrs,
268fa516b66SMatthew Garrett };
269fa516b66SMatthew Garrett 
270fa516b66SMatthew Garrett static int evm_init_xattrs(void)
271fa516b66SMatthew Garrett {
272fa516b66SMatthew Garrett 	evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL,
273fa516b66SMatthew Garrett 					    &evm_xattr_ops);
274fa516b66SMatthew Garrett 	if (!evm_xattrs || IS_ERR(evm_xattrs))
275fa516b66SMatthew Garrett 		return -EFAULT;
276fa516b66SMatthew Garrett 
277fa516b66SMatthew Garrett 	return 0;
278fa516b66SMatthew Garrett }
279fa516b66SMatthew Garrett #else
280fa516b66SMatthew Garrett static int evm_init_xattrs(void)
281fa516b66SMatthew Garrett {
282fa516b66SMatthew Garrett 	return 0;
283fa516b66SMatthew Garrett }
284fa516b66SMatthew Garrett #endif
285fa516b66SMatthew Garrett 
28666dbc325SMimi Zohar int __init evm_init_secfs(void)
28766dbc325SMimi Zohar {
28866dbc325SMimi Zohar 	int error = 0;
28966dbc325SMimi Zohar 
2900c343af8SMatthew Garrett 	evm_dir = securityfs_create_dir("evm", integrity_dir);
2910c343af8SMatthew Garrett 	if (!evm_dir || IS_ERR(evm_dir))
2920c343af8SMatthew Garrett 		return -EFAULT;
2930c343af8SMatthew Garrett 
2940c343af8SMatthew Garrett 	evm_init_tpm = securityfs_create_file("evm", 0660,
2950c343af8SMatthew Garrett 					      evm_dir, NULL, &evm_key_ops);
2960c343af8SMatthew Garrett 	if (!evm_init_tpm || IS_ERR(evm_init_tpm)) {
29766dbc325SMimi Zohar 		error = -EFAULT;
2980c343af8SMatthew Garrett 		goto out;
2990c343af8SMatthew Garrett 	}
3000c343af8SMatthew Garrett 
3010c343af8SMatthew Garrett 	evm_symlink = securityfs_create_symlink("evm", NULL,
3020c343af8SMatthew Garrett 						"integrity/evm/evm", NULL);
3030c343af8SMatthew Garrett 	if (!evm_symlink || IS_ERR(evm_symlink)) {
3040c343af8SMatthew Garrett 		error = -EFAULT;
3050c343af8SMatthew Garrett 		goto out;
3060c343af8SMatthew Garrett 	}
3070c343af8SMatthew Garrett 
308fa516b66SMatthew Garrett 	if (evm_init_xattrs() != 0) {
309fa516b66SMatthew Garrett 		error = -EFAULT;
310fa516b66SMatthew Garrett 		goto out;
311fa516b66SMatthew Garrett 	}
312fa516b66SMatthew Garrett 
3130c343af8SMatthew Garrett 	return 0;
3140c343af8SMatthew Garrett out:
3150c343af8SMatthew Garrett 	securityfs_remove(evm_symlink);
3160c343af8SMatthew Garrett 	securityfs_remove(evm_init_tpm);
3170c343af8SMatthew Garrett 	securityfs_remove(evm_dir);
31866dbc325SMimi Zohar 	return error;
31966dbc325SMimi Zohar }
320