xref: /openbmc/linux/security/integrity/ima/ima_appraise.c (revision d3634d0f426bdeb433cb288bdbb0a5e16cf3dbbf)
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  */
37d26e1936SDmitry 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 
45def3e8b9SDmitry Kasatkin static int ima_fix_xattr(struct dentry *dentry,
462fe5d6deSMimi Zohar 			 struct integrity_iint_cache *iint)
472fe5d6deSMimi Zohar {
48c7c8bb23SDmitry Kasatkin 	iint->ima_hash.type = IMA_XATTR_DIGEST;
49def3e8b9SDmitry Kasatkin 	return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
50c7c8bb23SDmitry Kasatkin 				     &iint->ima_hash.type,
51c7c8bb23SDmitry Kasatkin 				     1 + iint->ima_hash.length, 0);
522fe5d6deSMimi Zohar }
532fe5d6deSMimi Zohar 
54d79d72e0SMimi Zohar /* Return specific func appraised cached result */
55d79d72e0SMimi Zohar enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
56d79d72e0SMimi Zohar 					   int func)
57d79d72e0SMimi Zohar {
58d79d72e0SMimi Zohar 	switch (func) {
59d79d72e0SMimi Zohar 	case MMAP_CHECK:
60d79d72e0SMimi Zohar 		return iint->ima_mmap_status;
61d79d72e0SMimi Zohar 	case BPRM_CHECK:
62d79d72e0SMimi Zohar 		return iint->ima_bprm_status;
63d79d72e0SMimi Zohar 	case MODULE_CHECK:
64d79d72e0SMimi Zohar 		return iint->ima_module_status;
65d79d72e0SMimi Zohar 	case FILE_CHECK:
66d79d72e0SMimi Zohar 	default:
67d79d72e0SMimi Zohar 		return iint->ima_file_status;
68d79d72e0SMimi Zohar 	}
69d79d72e0SMimi Zohar }
70d79d72e0SMimi Zohar 
71d79d72e0SMimi Zohar static void ima_set_cache_status(struct integrity_iint_cache *iint,
72d79d72e0SMimi Zohar 				 int func, enum integrity_status status)
73d79d72e0SMimi Zohar {
74d79d72e0SMimi Zohar 	switch (func) {
75d79d72e0SMimi Zohar 	case MMAP_CHECK:
76d79d72e0SMimi Zohar 		iint->ima_mmap_status = status;
77d79d72e0SMimi Zohar 		break;
78d79d72e0SMimi Zohar 	case BPRM_CHECK:
79d79d72e0SMimi Zohar 		iint->ima_bprm_status = status;
80d79d72e0SMimi Zohar 		break;
81d79d72e0SMimi Zohar 	case MODULE_CHECK:
82d79d72e0SMimi Zohar 		iint->ima_module_status = status;
83d79d72e0SMimi Zohar 		break;
84d79d72e0SMimi Zohar 	case FILE_CHECK:
85d79d72e0SMimi Zohar 	default:
86d79d72e0SMimi Zohar 		iint->ima_file_status = status;
87d79d72e0SMimi Zohar 		break;
88d79d72e0SMimi Zohar 	}
89d79d72e0SMimi Zohar }
90d79d72e0SMimi Zohar 
91d79d72e0SMimi Zohar static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
92d79d72e0SMimi Zohar {
93d79d72e0SMimi Zohar 	switch (func) {
94d79d72e0SMimi Zohar 	case MMAP_CHECK:
95d79d72e0SMimi Zohar 		iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
96d79d72e0SMimi Zohar 		break;
97d79d72e0SMimi Zohar 	case BPRM_CHECK:
98d79d72e0SMimi Zohar 		iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
99d79d72e0SMimi Zohar 		break;
100d79d72e0SMimi Zohar 	case MODULE_CHECK:
101d79d72e0SMimi Zohar 		iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
102d79d72e0SMimi Zohar 		break;
103d79d72e0SMimi Zohar 	case FILE_CHECK:
104d79d72e0SMimi Zohar 	default:
105d79d72e0SMimi Zohar 		iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
106d79d72e0SMimi Zohar 		break;
107d79d72e0SMimi Zohar 	}
108d79d72e0SMimi Zohar }
109d79d72e0SMimi Zohar 
110*d3634d0fSDmitry Kasatkin void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len,
111*d3634d0fSDmitry Kasatkin 		       struct ima_digest_data *hash)
112*d3634d0fSDmitry Kasatkin {
113*d3634d0fSDmitry Kasatkin 	struct signature_v2_hdr *sig;
114*d3634d0fSDmitry Kasatkin 
115*d3634d0fSDmitry Kasatkin 	if (!xattr_value || xattr_len < 0 || xattr_len <= 1 + sizeof(*sig))
116*d3634d0fSDmitry Kasatkin 		return;
117*d3634d0fSDmitry Kasatkin 
118*d3634d0fSDmitry Kasatkin 	sig = (typeof(sig)) xattr_value->digest;
119*d3634d0fSDmitry Kasatkin 
120*d3634d0fSDmitry Kasatkin 	if (xattr_value->type != EVM_IMA_XATTR_DIGSIG || sig->version != 2)
121*d3634d0fSDmitry Kasatkin 		return;
122*d3634d0fSDmitry Kasatkin 
123*d3634d0fSDmitry Kasatkin 	hash->algo = sig->hash_algo;
124*d3634d0fSDmitry Kasatkin }
125*d3634d0fSDmitry Kasatkin 
126*d3634d0fSDmitry Kasatkin int ima_read_xattr(struct dentry *dentry,
127*d3634d0fSDmitry Kasatkin 		   struct evm_ima_xattr_data **xattr_value)
128*d3634d0fSDmitry Kasatkin {
129*d3634d0fSDmitry Kasatkin 	struct inode *inode = dentry->d_inode;
130*d3634d0fSDmitry Kasatkin 
131*d3634d0fSDmitry Kasatkin 	if (!inode->i_op->getxattr)
132*d3634d0fSDmitry Kasatkin 		return 0;
133*d3634d0fSDmitry Kasatkin 
134*d3634d0fSDmitry Kasatkin 	return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value,
135*d3634d0fSDmitry Kasatkin 				  0, GFP_NOFS);
136*d3634d0fSDmitry Kasatkin }
137*d3634d0fSDmitry Kasatkin 
1382fe5d6deSMimi Zohar /*
1392fe5d6deSMimi Zohar  * ima_appraise_measurement - appraise file measurement
1402fe5d6deSMimi Zohar  *
1412fe5d6deSMimi Zohar  * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
1422fe5d6deSMimi Zohar  * Assuming success, compare the xattr hash with the collected measurement.
1432fe5d6deSMimi Zohar  *
1442fe5d6deSMimi Zohar  * Return 0 on success, error code otherwise
1452fe5d6deSMimi Zohar  */
146d79d72e0SMimi Zohar int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
147*d3634d0fSDmitry Kasatkin 			     struct file *file, const unsigned char *filename,
148*d3634d0fSDmitry Kasatkin 			     struct evm_ima_xattr_data *xattr_value,
149*d3634d0fSDmitry Kasatkin 			     int xattr_len)
1502fe5d6deSMimi Zohar {
1512fe5d6deSMimi Zohar 	struct dentry *dentry = file->f_dentry;
1522fe5d6deSMimi Zohar 	struct inode *inode = dentry->d_inode;
1532fe5d6deSMimi Zohar 	enum integrity_status status = INTEGRITY_UNKNOWN;
1542fe5d6deSMimi Zohar 	const char *op = "appraise_data";
1552fe5d6deSMimi Zohar 	char *cause = "unknown";
156*d3634d0fSDmitry Kasatkin 	int rc = xattr_len;
1572fe5d6deSMimi Zohar 
1582fe5d6deSMimi Zohar 	if (!ima_appraise)
1592fe5d6deSMimi Zohar 		return 0;
1602fe5d6deSMimi Zohar 	if (!inode->i_op->getxattr)
1612fe5d6deSMimi Zohar 		return INTEGRITY_UNKNOWN;
1622fe5d6deSMimi Zohar 
1632fe5d6deSMimi Zohar 	if (rc <= 0) {
1642fe5d6deSMimi Zohar 		if (rc && rc != -ENODATA)
1652fe5d6deSMimi Zohar 			goto out;
1662fe5d6deSMimi Zohar 
1672fe5d6deSMimi Zohar 		cause = "missing-hash";
1682fe5d6deSMimi Zohar 		status =
1692fe5d6deSMimi Zohar 		    (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
1702fe5d6deSMimi Zohar 		goto out;
1712fe5d6deSMimi Zohar 	}
1722fe5d6deSMimi Zohar 
1738606404fSDmitry Kasatkin 	status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
1742fe5d6deSMimi Zohar 	if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
1752fe5d6deSMimi Zohar 		if ((status == INTEGRITY_NOLABEL)
1762fe5d6deSMimi Zohar 		    || (status == INTEGRITY_NOXATTRS))
1772fe5d6deSMimi Zohar 			cause = "missing-HMAC";
1782fe5d6deSMimi Zohar 		else if (status == INTEGRITY_FAIL)
1792fe5d6deSMimi Zohar 			cause = "invalid-HMAC";
1802fe5d6deSMimi Zohar 		goto out;
1812fe5d6deSMimi Zohar 	}
1828606404fSDmitry Kasatkin 	switch (xattr_value->type) {
1838606404fSDmitry Kasatkin 	case IMA_XATTR_DIGEST:
1840e5a247cSDmitry Kasatkin 		if (iint->flags & IMA_DIGSIG_REQUIRED) {
1850e5a247cSDmitry Kasatkin 			cause = "IMA signature required";
1860e5a247cSDmitry Kasatkin 			status = INTEGRITY_FAIL;
1870e5a247cSDmitry Kasatkin 			break;
1880e5a247cSDmitry Kasatkin 		}
189*d3634d0fSDmitry Kasatkin 		if (xattr_len - 1 >= iint->ima_hash.length)
190*d3634d0fSDmitry Kasatkin 			/* xattr length may be longer. md5 hash in previous
191*d3634d0fSDmitry Kasatkin 			   version occupied 20 bytes in xattr, instead of 16
192*d3634d0fSDmitry Kasatkin 			 */
193c7c8bb23SDmitry Kasatkin 			rc = memcmp(xattr_value->digest,
194c7c8bb23SDmitry Kasatkin 				    iint->ima_hash.digest,
195c7c8bb23SDmitry Kasatkin 				    iint->ima_hash.length);
196c7c8bb23SDmitry Kasatkin 		else
197c7c8bb23SDmitry Kasatkin 			rc = -EINVAL;
1982fe5d6deSMimi Zohar 		if (rc) {
1992fe5d6deSMimi Zohar 			cause = "invalid-hash";
2008606404fSDmitry Kasatkin 			status = INTEGRITY_FAIL;
2018606404fSDmitry Kasatkin 			break;
2022fe5d6deSMimi Zohar 		}
2032fe5d6deSMimi Zohar 		status = INTEGRITY_PASS;
2048606404fSDmitry Kasatkin 		break;
2058606404fSDmitry Kasatkin 	case EVM_IMA_XATTR_DIGSIG:
2068606404fSDmitry Kasatkin 		iint->flags |= IMA_DIGSIG;
2078606404fSDmitry Kasatkin 		rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
2088606404fSDmitry Kasatkin 					     xattr_value->digest, rc - 1,
209c7c8bb23SDmitry Kasatkin 					     iint->ima_hash.digest,
210c7c8bb23SDmitry Kasatkin 					     iint->ima_hash.length);
2118606404fSDmitry Kasatkin 		if (rc == -EOPNOTSUPP) {
2128606404fSDmitry Kasatkin 			status = INTEGRITY_UNKNOWN;
2138606404fSDmitry Kasatkin 		} else if (rc) {
2148606404fSDmitry Kasatkin 			cause = "invalid-signature";
2158606404fSDmitry Kasatkin 			status = INTEGRITY_FAIL;
2168606404fSDmitry Kasatkin 		} else {
2178606404fSDmitry Kasatkin 			status = INTEGRITY_PASS;
2188606404fSDmitry Kasatkin 		}
2198606404fSDmitry Kasatkin 		break;
2208606404fSDmitry Kasatkin 	default:
2218606404fSDmitry Kasatkin 		status = INTEGRITY_UNKNOWN;
2228606404fSDmitry Kasatkin 		cause = "unknown-ima-data";
2238606404fSDmitry Kasatkin 		break;
2248606404fSDmitry Kasatkin 	}
2258606404fSDmitry Kasatkin 
2262fe5d6deSMimi Zohar out:
2272fe5d6deSMimi Zohar 	if (status != INTEGRITY_PASS) {
2288606404fSDmitry Kasatkin 		if ((ima_appraise & IMA_APPRAISE_FIX) &&
2298606404fSDmitry Kasatkin 		    (!xattr_value ||
2308606404fSDmitry Kasatkin 		     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
231def3e8b9SDmitry Kasatkin 			if (!ima_fix_xattr(dentry, iint))
2322fe5d6deSMimi Zohar 				status = INTEGRITY_PASS;
2332fe5d6deSMimi Zohar 		}
2342fe5d6deSMimi Zohar 		integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
2352fe5d6deSMimi Zohar 				    op, cause, rc, 0);
2368606404fSDmitry Kasatkin 	} else {
237d79d72e0SMimi Zohar 		ima_cache_flags(iint, func);
2382fe5d6deSMimi Zohar 	}
239d79d72e0SMimi Zohar 	ima_set_cache_status(iint, func, status);
2402fe5d6deSMimi Zohar 	return status;
2412fe5d6deSMimi Zohar }
2422fe5d6deSMimi Zohar 
2432fe5d6deSMimi Zohar /*
2442fe5d6deSMimi Zohar  * ima_update_xattr - update 'security.ima' hash value
2452fe5d6deSMimi Zohar  */
2462fe5d6deSMimi Zohar void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
2472fe5d6deSMimi Zohar {
2482fe5d6deSMimi Zohar 	struct dentry *dentry = file->f_dentry;
2492fe5d6deSMimi Zohar 	int rc = 0;
2502fe5d6deSMimi Zohar 
2518606404fSDmitry Kasatkin 	/* do not collect and update hash for digital signatures */
2528606404fSDmitry Kasatkin 	if (iint->flags & IMA_DIGSIG)
2538606404fSDmitry Kasatkin 		return;
2548606404fSDmitry Kasatkin 
255*d3634d0fSDmitry Kasatkin 	rc = ima_collect_measurement(iint, file, NULL, NULL);
2562fe5d6deSMimi Zohar 	if (rc < 0)
2572fe5d6deSMimi Zohar 		return;
2588606404fSDmitry Kasatkin 
2592fe5d6deSMimi Zohar 	ima_fix_xattr(dentry, iint);
2602fe5d6deSMimi Zohar }
2612fe5d6deSMimi Zohar 
2622fe5d6deSMimi Zohar /**
2632fe5d6deSMimi Zohar  * ima_inode_post_setattr - reflect file metadata changes
2642fe5d6deSMimi Zohar  * @dentry: pointer to the affected dentry
2652fe5d6deSMimi Zohar  *
2662fe5d6deSMimi Zohar  * Changes to a dentry's metadata might result in needing to appraise.
2672fe5d6deSMimi Zohar  *
2682fe5d6deSMimi Zohar  * This function is called from notify_change(), which expects the caller
2692fe5d6deSMimi Zohar  * to lock the inode's i_mutex.
2702fe5d6deSMimi Zohar  */
2712fe5d6deSMimi Zohar void ima_inode_post_setattr(struct dentry *dentry)
2722fe5d6deSMimi Zohar {
2732fe5d6deSMimi Zohar 	struct inode *inode = dentry->d_inode;
2742fe5d6deSMimi Zohar 	struct integrity_iint_cache *iint;
2752fe5d6deSMimi Zohar 	int must_appraise, rc;
2762fe5d6deSMimi Zohar 
2772fe5d6deSMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
2782fe5d6deSMimi Zohar 	    || !inode->i_op->removexattr)
2792fe5d6deSMimi Zohar 		return;
2802fe5d6deSMimi Zohar 
2812fe5d6deSMimi Zohar 	must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
2822fe5d6deSMimi Zohar 	iint = integrity_iint_find(inode);
2832fe5d6deSMimi Zohar 	if (iint) {
284d79d72e0SMimi Zohar 		iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
285d79d72e0SMimi Zohar 				 IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
286d79d72e0SMimi Zohar 				 IMA_ACTION_FLAGS);
2872fe5d6deSMimi Zohar 		if (must_appraise)
2882fe5d6deSMimi Zohar 			iint->flags |= IMA_APPRAISE;
2892fe5d6deSMimi Zohar 	}
2902fe5d6deSMimi Zohar 	if (!must_appraise)
2912fe5d6deSMimi Zohar 		rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
2922fe5d6deSMimi Zohar 	return;
2932fe5d6deSMimi Zohar }
29442c63330SMimi Zohar 
29542c63330SMimi Zohar /*
29642c63330SMimi Zohar  * ima_protect_xattr - protect 'security.ima'
29742c63330SMimi Zohar  *
29842c63330SMimi Zohar  * Ensure that not just anyone can modify or remove 'security.ima'.
29942c63330SMimi Zohar  */
30042c63330SMimi Zohar static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
30142c63330SMimi Zohar 			     const void *xattr_value, size_t xattr_value_len)
30242c63330SMimi Zohar {
30342c63330SMimi Zohar 	if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
30442c63330SMimi Zohar 		if (!capable(CAP_SYS_ADMIN))
30542c63330SMimi Zohar 			return -EPERM;
30642c63330SMimi Zohar 		return 1;
30742c63330SMimi Zohar 	}
30842c63330SMimi Zohar 	return 0;
30942c63330SMimi Zohar }
31042c63330SMimi Zohar 
31142c63330SMimi Zohar static void ima_reset_appraise_flags(struct inode *inode)
31242c63330SMimi Zohar {
31342c63330SMimi Zohar 	struct integrity_iint_cache *iint;
31442c63330SMimi Zohar 
31542c63330SMimi Zohar 	if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
31642c63330SMimi Zohar 		return;
31742c63330SMimi Zohar 
31842c63330SMimi Zohar 	iint = integrity_iint_find(inode);
31942c63330SMimi Zohar 	if (!iint)
32042c63330SMimi Zohar 		return;
32142c63330SMimi Zohar 
32245e2472eSDmitry Kasatkin 	iint->flags &= ~IMA_DONE_MASK;
32342c63330SMimi Zohar 	return;
32442c63330SMimi Zohar }
32542c63330SMimi Zohar 
32642c63330SMimi Zohar int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
32742c63330SMimi Zohar 		       const void *xattr_value, size_t xattr_value_len)
32842c63330SMimi Zohar {
32942c63330SMimi Zohar 	int result;
33042c63330SMimi Zohar 
33142c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, xattr_value,
33242c63330SMimi Zohar 				   xattr_value_len);
33342c63330SMimi Zohar 	if (result == 1) {
33442c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
33542c63330SMimi Zohar 		result = 0;
33642c63330SMimi Zohar 	}
33742c63330SMimi Zohar 	return result;
33842c63330SMimi Zohar }
33942c63330SMimi Zohar 
34042c63330SMimi Zohar int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
34142c63330SMimi Zohar {
34242c63330SMimi Zohar 	int result;
34342c63330SMimi Zohar 
34442c63330SMimi Zohar 	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
34542c63330SMimi Zohar 	if (result == 1) {
34642c63330SMimi Zohar 		ima_reset_appraise_flags(dentry->d_inode);
34742c63330SMimi Zohar 		result = 0;
34842c63330SMimi Zohar 	}
34942c63330SMimi Zohar 	return result;
35042c63330SMimi Zohar }
351