diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /security/integrity/ima/ima_main.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r-- | security/integrity/ima/ima_main.c | 310 |
1 files changed, 140 insertions, 170 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index b85e61bcf246..b2c89d9de2a4 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -13,14 +13,15 @@ | |||
13 | * License. | 13 | * License. |
14 | * | 14 | * |
15 | * File: ima_main.c | 15 | * File: ima_main.c |
16 | * implements the IMA hooks: ima_bprm_check, ima_file_mmap, | 16 | * implements the IMA hooks: ima_bprm_check, ima_file_mmap, |
17 | * and ima_path_check. | 17 | * and ima_file_check. |
18 | */ | 18 | */ |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/file.h> | 20 | #include <linux/file.h> |
21 | #include <linux/binfmts.h> | 21 | #include <linux/binfmts.h> |
22 | #include <linux/mount.h> | 22 | #include <linux/mount.h> |
23 | #include <linux/mman.h> | 23 | #include <linux/mman.h> |
24 | #include <linux/slab.h> | ||
24 | 25 | ||
25 | #include "ima.h" | 26 | #include "ima.h" |
26 | 27 | ||
@@ -35,50 +36,53 @@ static int __init hash_setup(char *str) | |||
35 | } | 36 | } |
36 | __setup("ima_hash=", hash_setup); | 37 | __setup("ima_hash=", hash_setup); |
37 | 38 | ||
38 | /** | 39 | struct ima_imbalance { |
39 | * ima_file_free - called on __fput() | 40 | struct hlist_node node; |
40 | * @file: pointer to file structure being freed | 41 | unsigned long fsmagic; |
42 | }; | ||
43 | |||
44 | /* | ||
45 | * ima_limit_imbalance - emit one imbalance message per filesystem type | ||
41 | * | 46 | * |
42 | * Flag files that changed, based on i_version; | 47 | * Maintain list of filesystem types that do not measure files properly. |
43 | * and decrement the iint readcount/writecount. | 48 | * Return false if unknown, true if known. |
44 | */ | 49 | */ |
45 | void ima_file_free(struct file *file) | 50 | static bool ima_limit_imbalance(struct file *file) |
46 | { | 51 | { |
47 | struct inode *inode = file->f_dentry->d_inode; | 52 | static DEFINE_SPINLOCK(ima_imbalance_lock); |
48 | struct ima_iint_cache *iint; | 53 | static HLIST_HEAD(ima_imbalance_list); |
49 | 54 | ||
50 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 55 | struct super_block *sb = file->f_dentry->d_sb; |
51 | return; | 56 | struct ima_imbalance *entry; |
52 | iint = ima_iint_find_get(inode); | 57 | struct hlist_node *node; |
53 | if (!iint) | 58 | bool found = false; |
54 | return; | 59 | |
55 | 60 | rcu_read_lock(); | |
56 | mutex_lock(&iint->mutex); | 61 | hlist_for_each_entry_rcu(entry, node, &ima_imbalance_list, node) { |
57 | if (iint->opencount <= 0) { | 62 | if (entry->fsmagic == sb->s_magic) { |
58 | printk(KERN_INFO | 63 | found = true; |
59 | "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n", | 64 | break; |
60 | __FUNCTION__, file->f_dentry->d_name.name, | ||
61 | iint->readcount, iint->writecount, | ||
62 | iint->opencount, atomic_long_read(&file->f_count)); | ||
63 | if (!(iint->flags & IMA_IINT_DUMP_STACK)) { | ||
64 | dump_stack(); | ||
65 | iint->flags |= IMA_IINT_DUMP_STACK; | ||
66 | } | 65 | } |
67 | } | 66 | } |
68 | iint->opencount--; | 67 | rcu_read_unlock(); |
69 | 68 | if (found) | |
70 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | 69 | goto out; |
71 | iint->readcount--; | ||
72 | 70 | ||
73 | if (file->f_mode & FMODE_WRITE) { | 71 | entry = kmalloc(sizeof(*entry), GFP_NOFS); |
74 | iint->writecount--; | 72 | if (!entry) |
75 | if (iint->writecount == 0) { | 73 | goto out; |
76 | if (iint->version != inode->i_version) | 74 | entry->fsmagic = sb->s_magic; |
77 | iint->flags &= ~IMA_MEASURED; | 75 | spin_lock(&ima_imbalance_lock); |
78 | } | 76 | /* |
79 | } | 77 | * we could have raced and something else might have added this fs |
80 | mutex_unlock(&iint->mutex); | 78 | * to the list, but we don't really care |
81 | kref_put(&iint->refcount, iint_free); | 79 | */ |
80 | hlist_add_head_rcu(&entry->node, &ima_imbalance_list); | ||
81 | spin_unlock(&ima_imbalance_lock); | ||
82 | printk(KERN_INFO "IMA: unmeasured files on fsmagic: %lX\n", | ||
83 | entry->fsmagic); | ||
84 | out: | ||
85 | return found; | ||
82 | } | 86 | } |
83 | 87 | ||
84 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. | 88 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. |
@@ -111,196 +115,142 @@ static void ima_read_write_check(enum iint_pcr_error error, | |||
111 | } | 115 | } |
112 | } | 116 | } |
113 | 117 | ||
114 | static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | 118 | /* |
115 | const unsigned char *filename) | 119 | * Update the counts given an fmode_t |
120 | */ | ||
121 | static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode) | ||
116 | { | 122 | { |
117 | int rc = 0; | 123 | BUG_ON(!mutex_is_locked(&iint->mutex)); |
118 | |||
119 | iint->opencount++; | ||
120 | iint->readcount++; | ||
121 | |||
122 | rc = ima_collect_measurement(iint, file); | ||
123 | if (!rc) | ||
124 | ima_store_measurement(iint, file, filename); | ||
125 | return rc; | ||
126 | } | ||
127 | 124 | ||
128 | static void ima_update_counts(struct ima_iint_cache *iint, int mask) | ||
129 | { | ||
130 | iint->opencount++; | 125 | iint->opencount++; |
131 | if ((mask & MAY_WRITE) || (mask == 0)) | 126 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) |
132 | iint->writecount++; | ||
133 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
134 | iint->readcount++; | 127 | iint->readcount++; |
128 | if (mode & FMODE_WRITE) | ||
129 | iint->writecount++; | ||
135 | } | 130 | } |
136 | 131 | ||
137 | /** | 132 | /* |
138 | * ima_path_check - based on policy, collect/store measurement. | 133 | * ima_counts_get - increment file counts |
139 | * @path: contains a pointer to the path to be measured | ||
140 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | ||
141 | * | ||
142 | * Measure the file being open for readonly, based on the | ||
143 | * ima_must_measure() policy decision. | ||
144 | * | 134 | * |
145 | * Keep read/write counters for all files, but only | 135 | * Maintain read/write counters for all files, but only |
146 | * invalidate the PCR for measured files: | 136 | * invalidate the PCR for measured files: |
147 | * - Opening a file for write when already open for read, | 137 | * - Opening a file for write when already open for read, |
148 | * results in a time of measure, time of use (ToMToU) error. | 138 | * results in a time of measure, time of use (ToMToU) error. |
149 | * - Opening a file for read when already open for write, | 139 | * - Opening a file for read when already open for write, |
150 | * could result in a file measurement error. | 140 | * could result in a file measurement error. |
151 | * | 141 | * |
152 | * Always return 0 and audit dentry_open failures. | ||
153 | * (Return code will be based upon measurement appraisal.) | ||
154 | */ | 142 | */ |
155 | int ima_path_check(struct path *path, int mask, int update_counts) | 143 | void ima_counts_get(struct file *file) |
156 | { | 144 | { |
157 | struct inode *inode = path->dentry->d_inode; | 145 | struct dentry *dentry = file->f_path.dentry; |
146 | struct inode *inode = dentry->d_inode; | ||
147 | fmode_t mode = file->f_mode; | ||
158 | struct ima_iint_cache *iint; | 148 | struct ima_iint_cache *iint; |
159 | struct file *file = NULL; | ||
160 | int rc; | 149 | int rc; |
161 | 150 | ||
162 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 151 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
163 | return 0; | 152 | return; |
164 | iint = ima_iint_find_insert_get(inode); | 153 | iint = ima_iint_find_get(inode); |
165 | if (!iint) | 154 | if (!iint) |
166 | return 0; | 155 | return; |
167 | |||
168 | mutex_lock(&iint->mutex); | 156 | mutex_lock(&iint->mutex); |
169 | if (update_counts) | 157 | rc = ima_must_measure(iint, inode, MAY_READ, FILE_CHECK); |
170 | ima_update_counts(iint, mask); | ||
171 | |||
172 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); | ||
173 | if (rc < 0) | 158 | if (rc < 0) |
174 | goto out; | 159 | goto out; |
175 | 160 | ||
176 | if ((mask & MAY_WRITE) || (mask == 0)) | 161 | if (mode & FMODE_WRITE) { |
177 | ima_read_write_check(TOMTOU, iint, inode, | 162 | ima_read_write_check(TOMTOU, iint, inode, dentry->d_name.name); |
178 | path->dentry->d_name.name); | ||
179 | |||
180 | if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ) | ||
181 | goto out; | 163 | goto out; |
182 | |||
183 | ima_read_write_check(OPEN_WRITERS, iint, inode, | ||
184 | path->dentry->d_name.name); | ||
185 | if (!(iint->flags & IMA_MEASURED)) { | ||
186 | struct dentry *dentry = dget(path->dentry); | ||
187 | struct vfsmount *mnt = mntget(path->mnt); | ||
188 | |||
189 | file = dentry_open(dentry, mnt, O_RDONLY | O_LARGEFILE, | ||
190 | current_cred()); | ||
191 | if (IS_ERR(file)) { | ||
192 | int audit_info = 0; | ||
193 | |||
194 | integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, | ||
195 | dentry->d_name.name, | ||
196 | "add_measurement", | ||
197 | "dentry_open failed", | ||
198 | 1, audit_info); | ||
199 | file = NULL; | ||
200 | goto out; | ||
201 | } | ||
202 | rc = get_path_measurement(iint, file, dentry->d_name.name); | ||
203 | } | 164 | } |
165 | ima_read_write_check(OPEN_WRITERS, iint, inode, dentry->d_name.name); | ||
204 | out: | 166 | out: |
167 | ima_inc_counts(iint, file->f_mode); | ||
205 | mutex_unlock(&iint->mutex); | 168 | mutex_unlock(&iint->mutex); |
206 | if (file) | 169 | |
207 | fput(file); | ||
208 | kref_put(&iint->refcount, iint_free); | 170 | kref_put(&iint->refcount, iint_free); |
209 | return 0; | ||
210 | } | 171 | } |
211 | EXPORT_SYMBOL_GPL(ima_path_check); | ||
212 | 172 | ||
213 | static int process_measurement(struct file *file, const unsigned char *filename, | 173 | /* |
214 | int mask, int function) | 174 | * Decrement ima counts |
175 | */ | ||
176 | static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode, | ||
177 | struct file *file) | ||
215 | { | 178 | { |
216 | struct inode *inode = file->f_dentry->d_inode; | 179 | mode_t mode = file->f_mode; |
217 | struct ima_iint_cache *iint; | 180 | BUG_ON(!mutex_is_locked(&iint->mutex)); |
218 | int rc; | ||
219 | 181 | ||
220 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 182 | iint->opencount--; |
221 | return 0; | 183 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) |
222 | iint = ima_iint_find_insert_get(inode); | 184 | iint->readcount--; |
223 | if (!iint) | 185 | if (mode & FMODE_WRITE) { |
224 | return -ENOMEM; | 186 | iint->writecount--; |
225 | 187 | if (iint->writecount == 0) { | |
226 | mutex_lock(&iint->mutex); | 188 | if (iint->version != inode->i_version) |
227 | rc = ima_must_measure(iint, inode, mask, function); | 189 | iint->flags &= ~IMA_MEASURED; |
228 | if (rc != 0) | 190 | } |
229 | goto out; | 191 | } |
230 | 192 | ||
231 | rc = ima_collect_measurement(iint, file); | 193 | if (((iint->opencount < 0) || |
232 | if (!rc) | 194 | (iint->readcount < 0) || |
233 | ima_store_measurement(iint, file, filename); | 195 | (iint->writecount < 0)) && |
234 | out: | 196 | !ima_limit_imbalance(file)) { |
235 | mutex_unlock(&iint->mutex); | 197 | printk(KERN_INFO "%s: open/free imbalance (r:%ld w:%ld o:%ld)\n", |
236 | kref_put(&iint->refcount, iint_free); | 198 | __FUNCTION__, iint->readcount, iint->writecount, |
237 | return rc; | 199 | iint->opencount); |
200 | dump_stack(); | ||
201 | } | ||
238 | } | 202 | } |
239 | 203 | ||
240 | /* | 204 | /** |
241 | * ima_counts_put - decrement file counts | 205 | * ima_file_free - called on __fput() |
206 | * @file: pointer to file structure being freed | ||
242 | * | 207 | * |
243 | * File counts are incremented in ima_path_check. On file open | 208 | * Flag files that changed, based on i_version; |
244 | * error, such as ETXTBSY, decrement the counts to prevent | 209 | * and decrement the iint readcount/writecount. |
245 | * unnecessary imbalance messages. | ||
246 | */ | 210 | */ |
247 | void ima_counts_put(struct path *path, int mask) | 211 | void ima_file_free(struct file *file) |
248 | { | 212 | { |
249 | struct inode *inode = path->dentry->d_inode; | 213 | struct inode *inode = file->f_dentry->d_inode; |
250 | struct ima_iint_cache *iint; | 214 | struct ima_iint_cache *iint; |
251 | 215 | ||
252 | /* The inode may already have been freed, freeing the iint | 216 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
253 | * with it. Verify the inode is not NULL before dereferencing | ||
254 | * it. | ||
255 | */ | ||
256 | if (!ima_initialized || !inode || !S_ISREG(inode->i_mode)) | ||
257 | return; | 217 | return; |
258 | iint = ima_iint_find_insert_get(inode); | 218 | iint = ima_iint_find_get(inode); |
259 | if (!iint) | 219 | if (!iint) |
260 | return; | 220 | return; |
261 | 221 | ||
262 | mutex_lock(&iint->mutex); | 222 | mutex_lock(&iint->mutex); |
263 | iint->opencount--; | 223 | ima_dec_counts(iint, inode, file); |
264 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
265 | iint->writecount--; | ||
266 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
267 | iint->readcount--; | ||
268 | mutex_unlock(&iint->mutex); | 224 | mutex_unlock(&iint->mutex); |
269 | |||
270 | kref_put(&iint->refcount, iint_free); | 225 | kref_put(&iint->refcount, iint_free); |
271 | } | 226 | } |
272 | 227 | ||
273 | /* | 228 | static int process_measurement(struct file *file, const unsigned char *filename, |
274 | * ima_counts_get - increment file counts | 229 | int mask, int function) |
275 | * | ||
276 | * - for IPC shm and shmat file. | ||
277 | * - for nfsd exported files. | ||
278 | * | ||
279 | * Increment the counts for these files to prevent unnecessary | ||
280 | * imbalance messages. | ||
281 | */ | ||
282 | void ima_counts_get(struct file *file) | ||
283 | { | 230 | { |
284 | struct inode *inode = file->f_dentry->d_inode; | 231 | struct inode *inode = file->f_dentry->d_inode; |
285 | struct ima_iint_cache *iint; | 232 | struct ima_iint_cache *iint; |
233 | int rc; | ||
286 | 234 | ||
287 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 235 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
288 | return; | 236 | return 0; |
289 | iint = ima_iint_find_insert_get(inode); | 237 | iint = ima_iint_find_get(inode); |
290 | if (!iint) | 238 | if (!iint) |
291 | return; | 239 | return -ENOMEM; |
240 | |||
292 | mutex_lock(&iint->mutex); | 241 | mutex_lock(&iint->mutex); |
293 | iint->opencount++; | 242 | rc = ima_must_measure(iint, inode, mask, function); |
294 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | 243 | if (rc != 0) |
295 | iint->readcount++; | 244 | goto out; |
296 | 245 | ||
297 | if (file->f_mode & FMODE_WRITE) | 246 | rc = ima_collect_measurement(iint, file); |
298 | iint->writecount++; | 247 | if (!rc) |
248 | ima_store_measurement(iint, file, filename); | ||
249 | out: | ||
299 | mutex_unlock(&iint->mutex); | 250 | mutex_unlock(&iint->mutex); |
300 | |||
301 | kref_put(&iint->refcount, iint_free); | 251 | kref_put(&iint->refcount, iint_free); |
252 | return rc; | ||
302 | } | 253 | } |
303 | EXPORT_SYMBOL_GPL(ima_counts_get); | ||
304 | 254 | ||
305 | /** | 255 | /** |
306 | * ima_file_mmap - based on policy, collect/store measurement. | 256 | * ima_file_mmap - based on policy, collect/store measurement. |
@@ -347,11 +297,31 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
347 | return 0; | 297 | return 0; |
348 | } | 298 | } |
349 | 299 | ||
300 | /** | ||
301 | * ima_path_check - based on policy, collect/store measurement. | ||
302 | * @file: pointer to the file to be measured | ||
303 | * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE | ||
304 | * | ||
305 | * Measure files based on the ima_must_measure() policy decision. | ||
306 | * | ||
307 | * Always return 0 and audit dentry_open failures. | ||
308 | * (Return code will be based upon measurement appraisal.) | ||
309 | */ | ||
310 | int ima_file_check(struct file *file, int mask) | ||
311 | { | ||
312 | int rc; | ||
313 | |||
314 | rc = process_measurement(file, file->f_dentry->d_name.name, | ||
315 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | ||
316 | FILE_CHECK); | ||
317 | return 0; | ||
318 | } | ||
319 | EXPORT_SYMBOL_GPL(ima_file_check); | ||
320 | |||
350 | static int __init init_ima(void) | 321 | static int __init init_ima(void) |
351 | { | 322 | { |
352 | int error; | 323 | int error; |
353 | 324 | ||
354 | ima_iintcache_init(); | ||
355 | error = ima_init(); | 325 | error = ima_init(); |
356 | ima_initialized = 1; | 326 | ima_initialized = 1; |
357 | return error; | 327 | return error; |