xref: /openbmc/linux/security/integrity/ima/ima_api.c (revision fd589a8f)
1 /*
2  * Copyright (C) 2008 IBM Corporation
3  *
4  * Author: Mimi Zohar <zohar@us.ibm.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, version 2 of the
9  * License.
10  *
11  * File: ima_api.c
12  *	Implements must_measure, collect_measurement, store_measurement,
13  *	and store_template.
14  */
15 #include <linux/module.h>
16 
17 #include "ima.h"
18 static const char *IMA_TEMPLATE_NAME = "ima";
19 
20 /*
21  * ima_store_template - store ima template measurements
22  *
23  * Calculate the hash of a template entry, add the template entry
24  * to an ordered list of measurement entries maintained inside the kernel,
25  * and also update the aggregate integrity value (maintained inside the
26  * configured TPM PCR) over the hashes of the current list of measurement
27  * entries.
28  *
29  * Applications retrieve the current kernel-held measurement list through
30  * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
31  * TPM PCR (called quote) can be retrieved using a TPM user space library
32  * and is used to validate the measurement list.
33  *
34  * Returns 0 on success, error code otherwise
35  */
36 int ima_store_template(struct ima_template_entry *entry,
37 		       int violation, struct inode *inode)
38 {
39 	const char *op = "add_template_measure";
40 	const char *audit_cause = "hashing_error";
41 	int result;
42 
43 	memset(entry->digest, 0, sizeof(entry->digest));
44 	entry->template_name = IMA_TEMPLATE_NAME;
45 	entry->template_len = sizeof(entry->template);
46 
47 	if (!violation) {
48 		result = ima_calc_template_hash(entry->template_len,
49 						&entry->template,
50 						entry->digest);
51 		if (result < 0) {
52 			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
53 					    entry->template_name, op,
54 					    audit_cause, result, 0);
55 			return result;
56 		}
57 	}
58 	result = ima_add_template_entry(entry, violation, op, inode);
59 	return result;
60 }
61 
62 /*
63  * ima_add_violation - add violation to measurement list.
64  *
65  * Violations are flagged in the measurement list with zero hash values.
66  * By extending the PCR with 0xFF's instead of with zeroes, the PCR
67  * value is invalidated.
68  */
69 void ima_add_violation(struct inode *inode, const unsigned char *filename,
70 		       const char *op, const char *cause)
71 {
72 	struct ima_template_entry *entry;
73 	int violation = 1;
74 	int result;
75 
76 	/* can overflow, only indicator */
77 	atomic_long_inc(&ima_htable.violations);
78 
79 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
80 	if (!entry) {
81 		result = -ENOMEM;
82 		goto err_out;
83 	}
84 	memset(&entry->template, 0, sizeof(entry->template));
85 	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
86 	result = ima_store_template(entry, violation, inode);
87 	if (result < 0)
88 		kfree(entry);
89 err_out:
90 	integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
91 			    op, cause, result, 0);
92 }
93 
94 /**
95  * ima_must_measure - measure decision based on policy.
96  * @inode: pointer to inode to measure
97  * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
98  * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
99  *
100  * The policy is defined in terms of keypairs:
101  * 		subj=, obj=, type=, func=, mask=, fsmagic=
102  *	subj,obj, and type: are LSM specific.
103  * 	func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
104  * 	mask: contains the permission mask
105  *	fsmagic: hex value
106  *
107  * Must be called with iint->mutex held.
108  *
109  * Return 0 to measure. Return 1 if already measured.
110  * For matching a DONT_MEASURE policy, no policy, or other
111  * error, return an error code.
112 */
113 int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
114 		     int mask, int function)
115 {
116 	int must_measure;
117 
118 	if (iint->flags & IMA_MEASURED)
119 		return 1;
120 
121 	must_measure = ima_match_policy(inode, function, mask);
122 	return must_measure ? 0 : -EACCES;
123 }
124 
125 /*
126  * ima_collect_measurement - collect file measurement
127  *
128  * Calculate the file hash, if it doesn't already exist,
129  * storing the measurement and i_version in the iint.
130  *
131  * Must be called with iint->mutex held.
132  *
133  * Return 0 on success, error code otherwise
134  */
135 int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
136 {
137 	int result = -EEXIST;
138 
139 	if (!(iint->flags & IMA_MEASURED)) {
140 		u64 i_version = file->f_dentry->d_inode->i_version;
141 
142 		memset(iint->digest, 0, IMA_DIGEST_SIZE);
143 		result = ima_calc_hash(file, iint->digest);
144 		if (!result)
145 			iint->version = i_version;
146 	}
147 	return result;
148 }
149 
150 /*
151  * ima_store_measurement - store file measurement
152  *
153  * Create an "ima" template and then store the template by calling
154  * ima_store_template.
155  *
156  * We only get here if the inode has not already been measured,
157  * but the measurement could already exist:
158  * 	- multiple copies of the same file on either the same or
159  *	  different filesystems.
160  *	- the inode was previously flushed as well as the iint info,
161  *	  containing the hashing info.
162  *
163  * Must be called with iint->mutex held.
164  */
165 void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
166 			   const unsigned char *filename)
167 {
168 	const char *op = "add_template_measure";
169 	const char *audit_cause = "ENOMEM";
170 	int result = -ENOMEM;
171 	struct inode *inode = file->f_dentry->d_inode;
172 	struct ima_template_entry *entry;
173 	int violation = 0;
174 
175 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
176 	if (!entry) {
177 		integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
178 				    op, audit_cause, result, 0);
179 		return;
180 	}
181 	memset(&entry->template, 0, sizeof(entry->template));
182 	memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
183 	strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
184 
185 	result = ima_store_template(entry, violation, inode);
186 	if (!result)
187 		iint->flags |= IMA_MEASURED;
188 	else
189 		kfree(entry);
190 }
191