xref: /openbmc/linux/security/integrity/ima/ima_appraise.c (revision 42c63330f2b05aa6077c1bfc2798c04afe54f6b2)
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  */
372fe5d6deSMimi Zohar int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
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 {
482fe5d6deSMimi Zohar 	iint->digest[0] = IMA_XATTR_DIGEST;
492fe5d6deSMimi Zohar 	__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
502fe5d6deSMimi Zohar 			      iint->digest, IMA_DIGEST_SIZE + 1, 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;
662fe5d6deSMimi Zohar 	u8 xattr_value[IMA_DIGEST_SIZE];
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 
802fe5d6deSMimi Zohar 	rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value,
812fe5d6deSMimi Zohar 				   IMA_DIGEST_SIZE);
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 
922fe5d6deSMimi Zohar 	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 
1022fe5d6deSMimi Zohar 	rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE);
1032fe5d6deSMimi Zohar 	if (rc) {
1042fe5d6deSMimi Zohar 		status = INTEGRITY_FAIL;
1052fe5d6deSMimi Zohar 		cause = "invalid-hash";
1062fe5d6deSMimi Zohar 		print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
1072fe5d6deSMimi Zohar 				     xattr_value, IMA_DIGEST_SIZE);
1082fe5d6deSMimi Zohar 		print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
1092fe5d6deSMimi Zohar 				     iint->digest, IMA_DIGEST_SIZE);
1102fe5d6deSMimi Zohar 		goto out;
1112fe5d6deSMimi Zohar 	}
1122fe5d6deSMimi Zohar 	status = INTEGRITY_PASS;
1132fe5d6deSMimi Zohar 	iint->flags |= IMA_APPRAISED;
1142fe5d6deSMimi Zohar out:
1152fe5d6deSMimi Zohar 	if (status != INTEGRITY_PASS) {
1162fe5d6deSMimi Zohar 		if (ima_appraise & IMA_APPRAISE_FIX) {
1172fe5d6deSMimi Zohar 			ima_fix_xattr(dentry, iint);
1182fe5d6deSMimi Zohar 			status = INTEGRITY_PASS;
1192fe5d6deSMimi Zohar 		}
1202fe5d6deSMimi Zohar 		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
1212fe5d6deSMimi Zohar 				    op, cause, rc, 0);
1222fe5d6deSMimi Zohar 	}
1232fe5d6deSMimi Zohar 	iint->ima_status = status;
1242fe5d6deSMimi Zohar 	return status;
1252fe5d6deSMimi Zohar }
1262fe5d6deSMimi Zohar 
1272fe5d6deSMimi Zohar /*
1282fe5d6deSMimi Zohar  * ima_update_xattr - update 'security.ima' hash value
1292fe5d6deSMimi Zohar  */
1302fe5d6deSMimi Zohar void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
1312fe5d6deSMimi Zohar {
1322fe5d6deSMimi Zohar 	struct dentry *dentry = file->f_dentry;
1332fe5d6deSMimi Zohar 	int rc = 0;
1342fe5d6deSMimi Zohar 
1352fe5d6deSMimi Zohar 	rc = ima_collect_measurement(iint, file);
1362fe5d6deSMimi Zohar 	if (rc < 0)
1372fe5d6deSMimi Zohar 		return;
1382fe5d6deSMimi Zohar 	ima_fix_xattr(dentry, iint);
1392fe5d6deSMimi Zohar }
1402fe5d6deSMimi Zohar 
1412fe5d6deSMimi Zohar /**
1422fe5d6deSMimi Zohar  * ima_inode_post_setattr - reflect file metadata changes
1432fe5d6deSMimi Zohar  * @dentry: pointer to the affected dentry
1442fe5d6deSMimi Zohar  *
1452fe5d6deSMimi Zohar  * Changes to a dentry's metadata might result in needing to appraise.
1462fe5d6deSMimi Zohar  *
1472fe5d6deSMimi Zohar  * This function is called from notify_change(), which expects the caller
1482fe5d6deSMimi Zohar  * to lock the inode's i_mutex.
1492fe5d6deSMimi Zohar  */
1502fe5d6deSMimi Zohar void ima_inode_post_setattr(struct dentry *dentry)
1512fe5d6deSMimi Zohar {
1522fe5d6deSMimi Zohar 	struct inode *inode = dentry->d_inode;
1532fe5d6deSMimi Zohar 	struct integrity_iint_cache *iint;
1542fe5d6deSMimi Zohar 	int must_appraise, rc;
1552fe5d6deSMimi Zohar 
1562fe5d6deSMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
1572fe5d6deSMimi Zohar 	    || !inode->i_op->removexattr)
1582fe5d6deSMimi Zohar 		return;
1592fe5d6deSMimi Zohar 
1602fe5d6deSMimi Zohar 	must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
1612fe5d6deSMimi Zohar 	iint = integrity_iint_find(inode);
1622fe5d6deSMimi Zohar 	if (iint) {
1632fe5d6deSMimi Zohar 		if (must_appraise)
1642fe5d6deSMimi Zohar 			iint->flags |= IMA_APPRAISE;
1652fe5d6deSMimi Zohar 		else
1662fe5d6deSMimi Zohar 			iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
1672fe5d6deSMimi Zohar 	}
1682fe5d6deSMimi Zohar 	if (!must_appraise)
1692fe5d6deSMimi Zohar 		rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
1702fe5d6deSMimi Zohar 	return;
1712fe5d6deSMimi Zohar }
172*42c63330SMimi Zohar 
173*42c63330SMimi Zohar /*
174*42c63330SMimi Zohar  * ima_protect_xattr - protect 'security.ima'
175*42c63330SMimi Zohar  *
176*42c63330SMimi Zohar  * Ensure that not just anyone can modify or remove 'security.ima'.
177*42c63330SMimi Zohar  */
178*42c63330SMimi Zohar static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
179*42c63330SMimi Zohar 			     const void *xattr_value, size_t xattr_value_len)
180*42c63330SMimi Zohar {
181*42c63330SMimi Zohar 	if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
182*42c63330SMimi Zohar 		if (!capable(CAP_SYS_ADMIN))
183*42c63330SMimi Zohar 			return -EPERM;
184*42c63330SMimi Zohar 		return 1;
185*42c63330SMimi Zohar 	}
186*42c63330SMimi Zohar 	return 0;
187*42c63330SMimi Zohar }
188*42c63330SMimi Zohar 
189*42c63330SMimi Zohar static void ima_reset_appraise_flags(struct inode *inode)
190*42c63330SMimi Zohar {
191*42c63330SMimi Zohar 	struct integrity_iint_cache *iint;
192*42c63330SMimi Zohar 
193*42c63330SMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
194*42c63330SMimi Zohar 		return;
195*42c63330SMimi Zohar 
196*42c63330SMimi Zohar 	iint = integrity_iint_find(inode);
197*42c63330SMimi Zohar 	if (!iint)
198*42c63330SMimi Zohar 		return;
199*42c63330SMimi Zohar 
200*42c63330SMimi Zohar 	iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED);
201*42c63330SMimi Zohar 	return;
202*42c63330SMimi Zohar }
203*42c63330SMimi Zohar 
204*42c63330SMimi Zohar int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
205*42c63330SMimi Zohar 		       const void *xattr_value, size_t xattr_value_len)
206*42c63330SMimi Zohar {
207*42c63330SMimi Zohar 	int result;
208*42c63330SMimi Zohar 
209*42c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, xattr_value,
210*42c63330SMimi Zohar 				   xattr_value_len);
211*42c63330SMimi Zohar 	if (result == 1) {
212*42c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
213*42c63330SMimi Zohar 		result = 0;
214*42c63330SMimi Zohar 	}
215*42c63330SMimi Zohar 	return result;
216*42c63330SMimi Zohar }
217*42c63330SMimi Zohar 
218*42c63330SMimi Zohar int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
219*42c63330SMimi Zohar {
220*42c63330SMimi Zohar 	int result;
221*42c63330SMimi Zohar 
222*42c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
223*42c63330SMimi Zohar 	if (result == 1) {
224*42c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
225*42c63330SMimi Zohar 		result = 0;
226*42c63330SMimi Zohar 	}
227*42c63330SMimi Zohar 	return result;
228*42c63330SMimi Zohar }
229