diff options
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r-- | security/integrity/ima/ima_main.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c new file mode 100644 index 000000000000..53cee4c512ce --- /dev/null +++ b/security/integrity/ima/ima_main.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005,2006,2007,2008 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Reiner Sailer <sailer@watson.ibm.com> | ||
6 | * Serge Hallyn <serue@us.ibm.com> | ||
7 | * Kylene Hall <kylene@us.ibm.com> | ||
8 | * Mimi Zohar <zohar@us.ibm.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation, version 2 of the | ||
13 | * License. | ||
14 | * | ||
15 | * File: ima_main.c | ||
16 | * implements the IMA hooks: ima_bprm_check, ima_file_mmap, | ||
17 | * and ima_path_check. | ||
18 | */ | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/file.h> | ||
21 | #include <linux/binfmts.h> | ||
22 | #include <linux/mount.h> | ||
23 | #include <linux/mman.h> | ||
24 | |||
25 | #include "ima.h" | ||
26 | |||
27 | int ima_initialized; | ||
28 | |||
29 | char *ima_hash = "sha1"; | ||
30 | static int __init hash_setup(char *str) | ||
31 | { | ||
32 | const char *op = "hash_setup"; | ||
33 | const char *hash = "sha1"; | ||
34 | int result = 0; | ||
35 | int audit_info = 0; | ||
36 | |||
37 | if (strncmp(str, "md5", 3) == 0) { | ||
38 | hash = "md5"; | ||
39 | ima_hash = str; | ||
40 | } else if (strncmp(str, "sha1", 4) != 0) { | ||
41 | hash = "invalid_hash_type"; | ||
42 | result = 1; | ||
43 | } | ||
44 | integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash, | ||
45 | result, audit_info); | ||
46 | return 1; | ||
47 | } | ||
48 | __setup("ima_hash=", hash_setup); | ||
49 | |||
50 | /** | ||
51 | * ima_file_free - called on __fput() | ||
52 | * @file: pointer to file structure being freed | ||
53 | * | ||
54 | * Flag files that changed, based on i_version; | ||
55 | * and decrement the iint readcount/writecount. | ||
56 | */ | ||
57 | void ima_file_free(struct file *file) | ||
58 | { | ||
59 | struct inode *inode = file->f_dentry->d_inode; | ||
60 | struct ima_iint_cache *iint; | ||
61 | |||
62 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
63 | return; | ||
64 | iint = ima_iint_find_get(inode); | ||
65 | if (!iint) | ||
66 | return; | ||
67 | |||
68 | mutex_lock(&iint->mutex); | ||
69 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
70 | iint->readcount--; | ||
71 | |||
72 | if (file->f_mode & FMODE_WRITE) { | ||
73 | iint->writecount--; | ||
74 | if (iint->writecount == 0) { | ||
75 | if (iint->version != inode->i_version) | ||
76 | iint->flags &= ~IMA_MEASURED; | ||
77 | } | ||
78 | } | ||
79 | mutex_unlock(&iint->mutex); | ||
80 | kref_put(&iint->refcount, iint_free); | ||
81 | } | ||
82 | |||
83 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. | ||
84 | * | ||
85 | * When opening a file for read, if the file is already open for write, | ||
86 | * the file could change, resulting in a file measurement error. | ||
87 | * | ||
88 | * Opening a file for write, if the file is already open for read, results | ||
89 | * in a time of measure, time of use (ToMToU) error. | ||
90 | * | ||
91 | * In either case invalidate the PCR. | ||
92 | */ | ||
93 | enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; | ||
94 | static void ima_read_write_check(enum iint_pcr_error error, | ||
95 | struct ima_iint_cache *iint, | ||
96 | struct inode *inode, | ||
97 | const unsigned char *filename) | ||
98 | { | ||
99 | switch (error) { | ||
100 | case TOMTOU: | ||
101 | if (iint->readcount > 0) | ||
102 | ima_add_violation(inode, filename, "invalid_pcr", | ||
103 | "ToMToU"); | ||
104 | break; | ||
105 | case OPEN_WRITERS: | ||
106 | if (iint->writecount > 0) | ||
107 | ima_add_violation(inode, filename, "invalid_pcr", | ||
108 | "open_writers"); | ||
109 | break; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | ||
114 | const unsigned char *filename) | ||
115 | { | ||
116 | int rc = 0; | ||
117 | |||
118 | if (IS_ERR(file)) { | ||
119 | pr_info("%s dentry_open failed\n", filename); | ||
120 | return rc; | ||
121 | } | ||
122 | iint->readcount++; | ||
123 | |||
124 | rc = ima_collect_measurement(iint, file); | ||
125 | if (!rc) | ||
126 | ima_store_measurement(iint, file, filename); | ||
127 | return rc; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * ima_path_check - based on policy, collect/store measurement. | ||
132 | * @path: contains a pointer to the path to be measured | ||
133 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | ||
134 | * | ||
135 | * Measure the file being open for readonly, based on the | ||
136 | * ima_must_measure() policy decision. | ||
137 | * | ||
138 | * Keep read/write counters for all files, but only | ||
139 | * invalidate the PCR for measured files: | ||
140 | * - Opening a file for write when already open for read, | ||
141 | * results in a time of measure, time of use (ToMToU) error. | ||
142 | * - Opening a file for read when already open for write, | ||
143 | * could result in a file measurement error. | ||
144 | * | ||
145 | * Return 0 on success, an error code on failure. | ||
146 | * (Based on the results of appraise_measurement().) | ||
147 | */ | ||
148 | int ima_path_check(struct path *path, int mask) | ||
149 | { | ||
150 | struct inode *inode = path->dentry->d_inode; | ||
151 | struct ima_iint_cache *iint; | ||
152 | struct file *file = NULL; | ||
153 | int rc; | ||
154 | |||
155 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
156 | return 0; | ||
157 | iint = ima_iint_find_insert_get(inode); | ||
158 | if (!iint) | ||
159 | return 0; | ||
160 | |||
161 | mutex_lock(&iint->mutex); | ||
162 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
163 | iint->writecount++; | ||
164 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
165 | iint->readcount++; | ||
166 | |||
167 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); | ||
168 | if (rc < 0) | ||
169 | goto out; | ||
170 | |||
171 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
172 | ima_read_write_check(TOMTOU, iint, inode, | ||
173 | path->dentry->d_name.name); | ||
174 | |||
175 | if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ) | ||
176 | goto out; | ||
177 | |||
178 | ima_read_write_check(OPEN_WRITERS, iint, inode, | ||
179 | path->dentry->d_name.name); | ||
180 | if (!(iint->flags & IMA_MEASURED)) { | ||
181 | struct dentry *dentry = dget(path->dentry); | ||
182 | struct vfsmount *mnt = mntget(path->mnt); | ||
183 | |||
184 | file = dentry_open(dentry, mnt, O_RDONLY, current->cred); | ||
185 | rc = get_path_measurement(iint, file, dentry->d_name.name); | ||
186 | } | ||
187 | out: | ||
188 | mutex_unlock(&iint->mutex); | ||
189 | if (file) | ||
190 | fput(file); | ||
191 | kref_put(&iint->refcount, iint_free); | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int process_measurement(struct file *file, const unsigned char *filename, | ||
196 | int mask, int function) | ||
197 | { | ||
198 | struct inode *inode = file->f_dentry->d_inode; | ||
199 | struct ima_iint_cache *iint; | ||
200 | int rc; | ||
201 | |||
202 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | ||
203 | return 0; | ||
204 | iint = ima_iint_find_insert_get(inode); | ||
205 | if (!iint) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | mutex_lock(&iint->mutex); | ||
209 | rc = ima_must_measure(iint, inode, mask, function); | ||
210 | if (rc != 0) | ||
211 | goto out; | ||
212 | |||
213 | rc = ima_collect_measurement(iint, file); | ||
214 | if (!rc) | ||
215 | ima_store_measurement(iint, file, filename); | ||
216 | out: | ||
217 | mutex_unlock(&iint->mutex); | ||
218 | kref_put(&iint->refcount, iint_free); | ||
219 | return rc; | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * ima_file_mmap - based on policy, collect/store measurement. | ||
224 | * @file: pointer to the file to be measured (May be NULL) | ||
225 | * @prot: contains the protection that will be applied by the kernel. | ||
226 | * | ||
227 | * Measure files being mmapped executable based on the ima_must_measure() | ||
228 | * policy decision. | ||
229 | * | ||
230 | * Return 0 on success, an error code on failure. | ||
231 | * (Based on the results of appraise_measurement().) | ||
232 | */ | ||
233 | int ima_file_mmap(struct file *file, unsigned long prot) | ||
234 | { | ||
235 | int rc; | ||
236 | |||
237 | if (!file) | ||
238 | return 0; | ||
239 | if (prot & PROT_EXEC) | ||
240 | rc = process_measurement(file, file->f_dentry->d_name.name, | ||
241 | MAY_EXEC, FILE_MMAP); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * ima_bprm_check - based on policy, collect/store measurement. | ||
247 | * @bprm: contains the linux_binprm structure | ||
248 | * | ||
249 | * The OS protects against an executable file, already open for write, | ||
250 | * from being executed in deny_write_access() and an executable file, | ||
251 | * already open for execute, from being modified in get_write_access(). | ||
252 | * So we can be certain that what we verify and measure here is actually | ||
253 | * what is being executed. | ||
254 | * | ||
255 | * Return 0 on success, an error code on failure. | ||
256 | * (Based on the results of appraise_measurement().) | ||
257 | */ | ||
258 | int ima_bprm_check(struct linux_binprm *bprm) | ||
259 | { | ||
260 | int rc; | ||
261 | |||
262 | rc = process_measurement(bprm->file, bprm->filename, | ||
263 | MAY_EXEC, BPRM_CHECK); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int __init init_ima(void) | ||
268 | { | ||
269 | int error; | ||
270 | |||
271 | ima_iintcache_init(); | ||
272 | error = ima_init(); | ||
273 | ima_initialized = 1; | ||
274 | return error; | ||
275 | } | ||
276 | |||
277 | late_initcall(init_ima); /* Start IMA after the TPM is available */ | ||
278 | |||
279 | MODULE_DESCRIPTION("Integrity Measurement Architecture"); | ||
280 | MODULE_LICENSE("GPL"); | ||