xref: /openbmc/linux/security/integrity/ima/ima_appraise.c (revision d26e1936227b538a1691b978566ef269aef10853)
12fe5d6deSMimi Zohar /*
22fe5d6deSMimi Zohar  * Copyright (C) 2011 IBM Corporation
32fe5d6deSMimi Zohar  *
42fe5d6deSMimi Zohar  * Author:
52fe5d6deSMimi Zohar  * Mimi Zohar <zohar@us.ibm.com>
62fe5d6deSMimi Zohar  *
72fe5d6deSMimi Zohar  * This program is free software; you can redistribute it and/or modify
82fe5d6deSMimi Zohar  * it under the terms of the GNU General Public License as published by
92fe5d6deSMimi Zohar  * the Free Software Foundation, version 2 of the License.
102fe5d6deSMimi Zohar  */
112fe5d6deSMimi Zohar #include <linux/module.h>
122fe5d6deSMimi Zohar #include <linux/file.h>
132fe5d6deSMimi Zohar #include <linux/fs.h>
142fe5d6deSMimi Zohar #include <linux/xattr.h>
152fe5d6deSMimi Zohar #include <linux/magic.h>
162fe5d6deSMimi Zohar #include <linux/ima.h>
172fe5d6deSMimi Zohar #include <linux/evm.h>
182fe5d6deSMimi Zohar 
192fe5d6deSMimi Zohar #include "ima.h"
202fe5d6deSMimi Zohar 
212fe5d6deSMimi Zohar static int __init default_appraise_setup(char *str)
222fe5d6deSMimi Zohar {
232fe5d6deSMimi Zohar 	if (strncmp(str, "off", 3) == 0)
242fe5d6deSMimi Zohar 		ima_appraise = 0;
252fe5d6deSMimi Zohar 	else if (strncmp(str, "fix", 3) == 0)
262fe5d6deSMimi Zohar 		ima_appraise = IMA_APPRAISE_FIX;
272fe5d6deSMimi Zohar 	return 1;
282fe5d6deSMimi Zohar }
292fe5d6deSMimi Zohar 
302fe5d6deSMimi Zohar __setup("ima_appraise=", default_appraise_setup);
312fe5d6deSMimi Zohar 
322fe5d6deSMimi Zohar /*
332fe5d6deSMimi Zohar  * ima_must_appraise - set appraise flag
342fe5d6deSMimi Zohar  *
352fe5d6deSMimi Zohar  * Return 1 to appraise
362fe5d6deSMimi Zohar  */
37*d26e1936SDmitry Kasatkin int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
382fe5d6deSMimi Zohar {
3907f6a794SMimi Zohar 	if (!ima_appraise)
402fe5d6deSMimi Zohar 		return 0;
4107f6a794SMimi Zohar 
4207f6a794SMimi Zohar 	return ima_match_policy(inode, func, mask, IMA_APPRAISE);
432fe5d6deSMimi Zohar }
442fe5d6deSMimi Zohar 
452fe5d6deSMimi Zohar static void ima_fix_xattr(struct dentry *dentry,
462fe5d6deSMimi Zohar 			  struct integrity_iint_cache *iint)
472fe5d6deSMimi Zohar {
485a44b412SMimi Zohar 	iint->ima_xattr.type = IMA_XATTR_DIGEST;
495a44b412SMimi Zohar 	__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
505a44b412SMimi Zohar 			      sizeof iint->ima_xattr, 0);
512fe5d6deSMimi Zohar }
522fe5d6deSMimi Zohar 
532fe5d6deSMimi Zohar /*
542fe5d6deSMimi Zohar  * ima_appraise_measurement - appraise file measurement
552fe5d6deSMimi Zohar  *
562fe5d6deSMimi Zohar  * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
572fe5d6deSMimi Zohar  * Assuming success, compare the xattr hash with the collected measurement.
582fe5d6deSMimi Zohar  *
592fe5d6deSMimi Zohar  * Return 0 on success, error code otherwise
602fe5d6deSMimi Zohar  */
612fe5d6deSMimi Zohar int ima_appraise_measurement(struct integrity_iint_cache *iint,
622fe5d6deSMimi Zohar 			     struct file *file, const unsigned char *filename)
632fe5d6deSMimi Zohar {
642fe5d6deSMimi Zohar 	struct dentry *dentry = file->f_dentry;
652fe5d6deSMimi Zohar 	struct inode *inode = dentry->d_inode;
668606404fSDmitry Kasatkin 	struct evm_ima_xattr_data *xattr_value = NULL;
672fe5d6deSMimi Zohar 	enum integrity_status status = INTEGRITY_UNKNOWN;
682fe5d6deSMimi Zohar 	const char *op = "appraise_data";
692fe5d6deSMimi Zohar 	char *cause = "unknown";
702fe5d6deSMimi Zohar 	int rc;
712fe5d6deSMimi Zohar 
722fe5d6deSMimi Zohar 	if (!ima_appraise)
732fe5d6deSMimi Zohar 		return 0;
742fe5d6deSMimi Zohar 	if (!inode->i_op->getxattr)
752fe5d6deSMimi Zohar 		return INTEGRITY_UNKNOWN;
762fe5d6deSMimi Zohar 
772fe5d6deSMimi Zohar 	if (iint->flags & IMA_APPRAISED)
782fe5d6deSMimi Zohar 		return iint->ima_status;
792fe5d6deSMimi Zohar 
808606404fSDmitry Kasatkin 	rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
818606404fSDmitry Kasatkin 				0, GFP_NOFS);
822fe5d6deSMimi Zohar 	if (rc <= 0) {
832fe5d6deSMimi Zohar 		if (rc && rc != -ENODATA)
842fe5d6deSMimi Zohar 			goto out;
852fe5d6deSMimi Zohar 
862fe5d6deSMimi Zohar 		cause = "missing-hash";
872fe5d6deSMimi Zohar 		status =
882fe5d6deSMimi Zohar 		    (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
892fe5d6deSMimi Zohar 		goto out;
902fe5d6deSMimi Zohar 	}
912fe5d6deSMimi Zohar 
928606404fSDmitry Kasatkin 	status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
932fe5d6deSMimi Zohar 	if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
942fe5d6deSMimi Zohar 		if ((status == INTEGRITY_NOLABEL)
952fe5d6deSMimi Zohar 		    || (status == INTEGRITY_NOXATTRS))
962fe5d6deSMimi Zohar 			cause = "missing-HMAC";
972fe5d6deSMimi Zohar 		else if (status == INTEGRITY_FAIL)
982fe5d6deSMimi Zohar 			cause = "invalid-HMAC";
992fe5d6deSMimi Zohar 		goto out;
1002fe5d6deSMimi Zohar 	}
1012fe5d6deSMimi Zohar 
1028606404fSDmitry Kasatkin 	switch (xattr_value->type) {
1038606404fSDmitry Kasatkin 	case IMA_XATTR_DIGEST:
1048606404fSDmitry Kasatkin 		rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
1055a44b412SMimi Zohar 			    IMA_DIGEST_SIZE);
1062fe5d6deSMimi Zohar 		if (rc) {
1072fe5d6deSMimi Zohar 			cause = "invalid-hash";
1088606404fSDmitry Kasatkin 			status = INTEGRITY_FAIL;
1092fe5d6deSMimi Zohar 			print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
1108606404fSDmitry Kasatkin 					     xattr_value, sizeof(*xattr_value));
1112fe5d6deSMimi Zohar 			print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
1125a44b412SMimi Zohar 					     (u8 *)&iint->ima_xattr,
1135a44b412SMimi Zohar 					     sizeof iint->ima_xattr);
1148606404fSDmitry Kasatkin 			break;
1152fe5d6deSMimi Zohar 		}
1162fe5d6deSMimi Zohar 		status = INTEGRITY_PASS;
1178606404fSDmitry Kasatkin 		break;
1188606404fSDmitry Kasatkin 	case EVM_IMA_XATTR_DIGSIG:
1198606404fSDmitry Kasatkin 		iint->flags |= IMA_DIGSIG;
1208606404fSDmitry Kasatkin 		rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
1218606404fSDmitry Kasatkin 					     xattr_value->digest, rc - 1,
1228606404fSDmitry Kasatkin 					     iint->ima_xattr.digest,
1238606404fSDmitry Kasatkin 					     IMA_DIGEST_SIZE);
1248606404fSDmitry Kasatkin 		if (rc == -EOPNOTSUPP) {
1258606404fSDmitry Kasatkin 			status = INTEGRITY_UNKNOWN;
1268606404fSDmitry Kasatkin 		} else if (rc) {
1278606404fSDmitry Kasatkin 			cause = "invalid-signature";
1288606404fSDmitry Kasatkin 			status = INTEGRITY_FAIL;
1298606404fSDmitry Kasatkin 		} else {
1308606404fSDmitry Kasatkin 			status = INTEGRITY_PASS;
1318606404fSDmitry Kasatkin 		}
1328606404fSDmitry Kasatkin 		break;
1338606404fSDmitry Kasatkin 	default:
1348606404fSDmitry Kasatkin 		status = INTEGRITY_UNKNOWN;
1358606404fSDmitry Kasatkin 		cause = "unknown-ima-data";
1368606404fSDmitry Kasatkin 		break;
1378606404fSDmitry Kasatkin 	}
1388606404fSDmitry Kasatkin 
1392fe5d6deSMimi Zohar out:
1402fe5d6deSMimi Zohar 	if (status != INTEGRITY_PASS) {
1418606404fSDmitry Kasatkin 		if ((ima_appraise & IMA_APPRAISE_FIX) &&
1428606404fSDmitry Kasatkin 		    (!xattr_value ||
1438606404fSDmitry Kasatkin 		     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
1442fe5d6deSMimi Zohar 			ima_fix_xattr(dentry, iint);
1452fe5d6deSMimi Zohar 			status = INTEGRITY_PASS;
1462fe5d6deSMimi Zohar 		}
1472fe5d6deSMimi Zohar 		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
1482fe5d6deSMimi Zohar 				    op, cause, rc, 0);
1498606404fSDmitry Kasatkin 	} else {
1508606404fSDmitry Kasatkin 		iint->flags |= IMA_APPRAISED;
1512fe5d6deSMimi Zohar 	}
1522fe5d6deSMimi Zohar 	iint->ima_status = status;
1538606404fSDmitry Kasatkin 	kfree(xattr_value);
1542fe5d6deSMimi Zohar 	return status;
1552fe5d6deSMimi Zohar }
1562fe5d6deSMimi Zohar 
1572fe5d6deSMimi Zohar /*
1582fe5d6deSMimi Zohar  * ima_update_xattr - update 'security.ima' hash value
1592fe5d6deSMimi Zohar  */
1602fe5d6deSMimi Zohar void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
1612fe5d6deSMimi Zohar {
1622fe5d6deSMimi Zohar 	struct dentry *dentry = file->f_dentry;
1632fe5d6deSMimi Zohar 	int rc = 0;
1642fe5d6deSMimi Zohar 
1658606404fSDmitry Kasatkin 	/* do not collect and update hash for digital signatures */
1668606404fSDmitry Kasatkin 	if (iint->flags & IMA_DIGSIG)
1678606404fSDmitry Kasatkin 		return;
1688606404fSDmitry Kasatkin 
1692fe5d6deSMimi Zohar 	rc = ima_collect_measurement(iint, file);
1702fe5d6deSMimi Zohar 	if (rc < 0)
1712fe5d6deSMimi Zohar 		return;
1728606404fSDmitry Kasatkin 
1732fe5d6deSMimi Zohar 	ima_fix_xattr(dentry, iint);
1742fe5d6deSMimi Zohar }
1752fe5d6deSMimi Zohar 
1762fe5d6deSMimi Zohar /**
1772fe5d6deSMimi Zohar  * ima_inode_post_setattr - reflect file metadata changes
1782fe5d6deSMimi Zohar  * @dentry: pointer to the affected dentry
1792fe5d6deSMimi Zohar  *
1802fe5d6deSMimi Zohar  * Changes to a dentry's metadata might result in needing to appraise.
1812fe5d6deSMimi Zohar  *
1822fe5d6deSMimi Zohar  * This function is called from notify_change(), which expects the caller
1832fe5d6deSMimi Zohar  * to lock the inode's i_mutex.
1842fe5d6deSMimi Zohar  */
1852fe5d6deSMimi Zohar void ima_inode_post_setattr(struct dentry *dentry)
1862fe5d6deSMimi Zohar {
1872fe5d6deSMimi Zohar 	struct inode *inode = dentry->d_inode;
1882fe5d6deSMimi Zohar 	struct integrity_iint_cache *iint;
1892fe5d6deSMimi Zohar 	int must_appraise, rc;
1902fe5d6deSMimi Zohar 
1912fe5d6deSMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
1922fe5d6deSMimi Zohar 	    || !inode->i_op->removexattr)
1932fe5d6deSMimi Zohar 		return;
1942fe5d6deSMimi Zohar 
1952fe5d6deSMimi Zohar 	must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
1962fe5d6deSMimi Zohar 	iint = integrity_iint_find(inode);
1972fe5d6deSMimi Zohar 	if (iint) {
1982fe5d6deSMimi Zohar 		if (must_appraise)
1992fe5d6deSMimi Zohar 			iint->flags |= IMA_APPRAISE;
2002fe5d6deSMimi Zohar 		else
2012fe5d6deSMimi Zohar 			iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
2022fe5d6deSMimi Zohar 	}
2032fe5d6deSMimi Zohar 	if (!must_appraise)
2042fe5d6deSMimi Zohar 		rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
2052fe5d6deSMimi Zohar 	return;
2062fe5d6deSMimi Zohar }
20742c63330SMimi Zohar 
20842c63330SMimi Zohar /*
20942c63330SMimi Zohar  * ima_protect_xattr - protect 'security.ima'
21042c63330SMimi Zohar  *
21142c63330SMimi Zohar  * Ensure that not just anyone can modify or remove 'security.ima'.
21242c63330SMimi Zohar  */
21342c63330SMimi Zohar static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
21442c63330SMimi Zohar 			     const void *xattr_value, size_t xattr_value_len)
21542c63330SMimi Zohar {
21642c63330SMimi Zohar 	if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
21742c63330SMimi Zohar 		if (!capable(CAP_SYS_ADMIN))
21842c63330SMimi Zohar 			return -EPERM;
21942c63330SMimi Zohar 		return 1;
22042c63330SMimi Zohar 	}
22142c63330SMimi Zohar 	return 0;
22242c63330SMimi Zohar }
22342c63330SMimi Zohar 
22442c63330SMimi Zohar static void ima_reset_appraise_flags(struct inode *inode)
22542c63330SMimi Zohar {
22642c63330SMimi Zohar 	struct integrity_iint_cache *iint;
22742c63330SMimi Zohar 
22842c63330SMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
22942c63330SMimi Zohar 		return;
23042c63330SMimi Zohar 
23142c63330SMimi Zohar 	iint = integrity_iint_find(inode);
23242c63330SMimi Zohar 	if (!iint)
23342c63330SMimi Zohar 		return;
23442c63330SMimi Zohar 
23545e2472eSDmitry Kasatkin 	iint->flags &= ~IMA_DONE_MASK;
23642c63330SMimi Zohar 	return;
23742c63330SMimi Zohar }
23842c63330SMimi Zohar 
23942c63330SMimi Zohar int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
24042c63330SMimi Zohar 		       const void *xattr_value, size_t xattr_value_len)
24142c63330SMimi Zohar {
24242c63330SMimi Zohar 	int result;
24342c63330SMimi Zohar 
24442c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, xattr_value,
24542c63330SMimi Zohar 				   xattr_value_len);
24642c63330SMimi Zohar 	if (result == 1) {
24742c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
24842c63330SMimi Zohar 		result = 0;
24942c63330SMimi Zohar 	}
25042c63330SMimi Zohar 	return result;
25142c63330SMimi Zohar }
25242c63330SMimi Zohar 
25342c63330SMimi Zohar int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
25442c63330SMimi Zohar {
25542c63330SMimi Zohar 	int result;
25642c63330SMimi Zohar 
25742c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
25842c63330SMimi Zohar 	if (result == 1) {
25942c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
26042c63330SMimi Zohar 		result = 0;
26142c63330SMimi Zohar 	}
26242c63330SMimi Zohar 	return result;
26342c63330SMimi Zohar }
264