diff options
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r-- | security/integrity/ima/ima_main.c | 184 |
1 files changed, 103 insertions, 81 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index b85e61bcf246..a89f44d5e030 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -13,8 +13,8 @@ | |||
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_path_check. |
18 | */ | 18 | */ |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/file.h> | 20 | #include <linux/file.h> |
@@ -35,6 +35,100 @@ static int __init hash_setup(char *str) | |||
35 | } | 35 | } |
36 | __setup("ima_hash=", hash_setup); | 36 | __setup("ima_hash=", hash_setup); |
37 | 37 | ||
38 | struct ima_imbalance { | ||
39 | struct hlist_node node; | ||
40 | unsigned long fsmagic; | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * ima_limit_imbalance - emit one imbalance message per filesystem type | ||
45 | * | ||
46 | * Maintain list of filesystem types that do not measure files properly. | ||
47 | * Return false if unknown, true if known. | ||
48 | */ | ||
49 | static bool ima_limit_imbalance(struct file *file) | ||
50 | { | ||
51 | static DEFINE_SPINLOCK(ima_imbalance_lock); | ||
52 | static HLIST_HEAD(ima_imbalance_list); | ||
53 | |||
54 | struct super_block *sb = file->f_dentry->d_sb; | ||
55 | struct ima_imbalance *entry; | ||
56 | struct hlist_node *node; | ||
57 | bool found = false; | ||
58 | |||
59 | rcu_read_lock(); | ||
60 | hlist_for_each_entry_rcu(entry, node, &ima_imbalance_list, node) { | ||
61 | if (entry->fsmagic == sb->s_magic) { | ||
62 | found = true; | ||
63 | break; | ||
64 | } | ||
65 | } | ||
66 | rcu_read_unlock(); | ||
67 | if (found) | ||
68 | goto out; | ||
69 | |||
70 | entry = kmalloc(sizeof(*entry), GFP_NOFS); | ||
71 | if (!entry) | ||
72 | goto out; | ||
73 | entry->fsmagic = sb->s_magic; | ||
74 | spin_lock(&ima_imbalance_lock); | ||
75 | /* | ||
76 | * we could have raced and something else might have added this fs | ||
77 | * to the list, but we don't really care | ||
78 | */ | ||
79 | hlist_add_head_rcu(&entry->node, &ima_imbalance_list); | ||
80 | spin_unlock(&ima_imbalance_lock); | ||
81 | printk(KERN_INFO "IMA: unmeasured files on fsmagic: %lX\n", | ||
82 | entry->fsmagic); | ||
83 | out: | ||
84 | return found; | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Update the counts given an fmode_t | ||
89 | */ | ||
90 | static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode) | ||
91 | { | ||
92 | BUG_ON(!mutex_is_locked(&iint->mutex)); | ||
93 | |||
94 | iint->opencount++; | ||
95 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
96 | iint->readcount++; | ||
97 | if (mode & FMODE_WRITE) | ||
98 | iint->writecount++; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Decrement ima counts | ||
103 | */ | ||
104 | static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode, | ||
105 | struct file *file) | ||
106 | { | ||
107 | mode_t mode = file->f_mode; | ||
108 | BUG_ON(!mutex_is_locked(&iint->mutex)); | ||
109 | |||
110 | iint->opencount--; | ||
111 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
112 | iint->readcount--; | ||
113 | if (mode & FMODE_WRITE) { | ||
114 | iint->writecount--; | ||
115 | if (iint->writecount == 0) { | ||
116 | if (iint->version != inode->i_version) | ||
117 | iint->flags &= ~IMA_MEASURED; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | if (((iint->opencount < 0) || | ||
122 | (iint->readcount < 0) || | ||
123 | (iint->writecount < 0)) && | ||
124 | !ima_limit_imbalance(file)) { | ||
125 | printk(KERN_INFO "%s: open/free imbalance (r:%ld w:%ld o:%ld)\n", | ||
126 | __FUNCTION__, iint->readcount, iint->writecount, | ||
127 | iint->opencount); | ||
128 | dump_stack(); | ||
129 | } | ||
130 | } | ||
131 | |||
38 | /** | 132 | /** |
39 | * ima_file_free - called on __fput() | 133 | * ima_file_free - called on __fput() |
40 | * @file: pointer to file structure being freed | 134 | * @file: pointer to file structure being freed |
@@ -54,29 +148,7 @@ void ima_file_free(struct file *file) | |||
54 | return; | 148 | return; |
55 | 149 | ||
56 | mutex_lock(&iint->mutex); | 150 | mutex_lock(&iint->mutex); |
57 | if (iint->opencount <= 0) { | 151 | ima_dec_counts(iint, inode, file); |
58 | printk(KERN_INFO | ||
59 | "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n", | ||
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 | } | ||
67 | } | ||
68 | iint->opencount--; | ||
69 | |||
70 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
71 | iint->readcount--; | ||
72 | |||
73 | if (file->f_mode & FMODE_WRITE) { | ||
74 | iint->writecount--; | ||
75 | if (iint->writecount == 0) { | ||
76 | if (iint->version != inode->i_version) | ||
77 | iint->flags &= ~IMA_MEASURED; | ||
78 | } | ||
79 | } | ||
80 | mutex_unlock(&iint->mutex); | 152 | mutex_unlock(&iint->mutex); |
81 | kref_put(&iint->refcount, iint_free); | 153 | kref_put(&iint->refcount, iint_free); |
82 | } | 154 | } |
@@ -116,8 +188,7 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | |||
116 | { | 188 | { |
117 | int rc = 0; | 189 | int rc = 0; |
118 | 190 | ||
119 | iint->opencount++; | 191 | ima_inc_counts(iint, file->f_mode); |
120 | iint->readcount++; | ||
121 | 192 | ||
122 | rc = ima_collect_measurement(iint, file); | 193 | rc = ima_collect_measurement(iint, file); |
123 | if (!rc) | 194 | if (!rc) |
@@ -125,15 +196,6 @@ static int get_path_measurement(struct ima_iint_cache *iint, struct file *file, | |||
125 | return rc; | 196 | return rc; |
126 | } | 197 | } |
127 | 198 | ||
128 | static void ima_update_counts(struct ima_iint_cache *iint, int mask) | ||
129 | { | ||
130 | iint->opencount++; | ||
131 | if ((mask & MAY_WRITE) || (mask == 0)) | ||
132 | iint->writecount++; | ||
133 | else if (mask & (MAY_READ | MAY_EXEC)) | ||
134 | iint->readcount++; | ||
135 | } | ||
136 | |||
137 | /** | 199 | /** |
138 | * ima_path_check - based on policy, collect/store measurement. | 200 | * ima_path_check - based on policy, collect/store measurement. |
139 | * @path: contains a pointer to the path to be measured | 201 | * @path: contains a pointer to the path to be measured |
@@ -152,7 +214,7 @@ static void ima_update_counts(struct ima_iint_cache *iint, int mask) | |||
152 | * Always return 0 and audit dentry_open failures. | 214 | * Always return 0 and audit dentry_open failures. |
153 | * (Return code will be based upon measurement appraisal.) | 215 | * (Return code will be based upon measurement appraisal.) |
154 | */ | 216 | */ |
155 | int ima_path_check(struct path *path, int mask, int update_counts) | 217 | int ima_path_check(struct path *path, int mask) |
156 | { | 218 | { |
157 | struct inode *inode = path->dentry->d_inode; | 219 | struct inode *inode = path->dentry->d_inode; |
158 | struct ima_iint_cache *iint; | 220 | struct ima_iint_cache *iint; |
@@ -161,13 +223,11 @@ int ima_path_check(struct path *path, int mask, int update_counts) | |||
161 | 223 | ||
162 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 224 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
163 | return 0; | 225 | return 0; |
164 | iint = ima_iint_find_insert_get(inode); | 226 | iint = ima_iint_find_get(inode); |
165 | if (!iint) | 227 | if (!iint) |
166 | return 0; | 228 | return 0; |
167 | 229 | ||
168 | mutex_lock(&iint->mutex); | 230 | mutex_lock(&iint->mutex); |
169 | if (update_counts) | ||
170 | ima_update_counts(iint, mask); | ||
171 | 231 | ||
172 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); | 232 | rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK); |
173 | if (rc < 0) | 233 | if (rc < 0) |
@@ -219,7 +279,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
219 | 279 | ||
220 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 280 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
221 | return 0; | 281 | return 0; |
222 | iint = ima_iint_find_insert_get(inode); | 282 | iint = ima_iint_find_get(inode); |
223 | if (!iint) | 283 | if (!iint) |
224 | return -ENOMEM; | 284 | return -ENOMEM; |
225 | 285 | ||
@@ -238,39 +298,6 @@ out: | |||
238 | } | 298 | } |
239 | 299 | ||
240 | /* | 300 | /* |
241 | * ima_counts_put - decrement file counts | ||
242 | * | ||
243 | * File counts are incremented in ima_path_check. On file open | ||
244 | * error, such as ETXTBSY, decrement the counts to prevent | ||
245 | * unnecessary imbalance messages. | ||
246 | */ | ||
247 | void ima_counts_put(struct path *path, int mask) | ||
248 | { | ||
249 | struct inode *inode = path->dentry->d_inode; | ||
250 | struct ima_iint_cache *iint; | ||
251 | |||
252 | /* The inode may already have been freed, freeing the iint | ||
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; | ||
258 | iint = ima_iint_find_insert_get(inode); | ||
259 | if (!iint) | ||
260 | return; | ||
261 | |||
262 | mutex_lock(&iint->mutex); | ||
263 | iint->opencount--; | ||
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); | ||
269 | |||
270 | kref_put(&iint->refcount, iint_free); | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * ima_counts_get - increment file counts | 301 | * ima_counts_get - increment file counts |
275 | * | 302 | * |
276 | * - for IPC shm and shmat file. | 303 | * - for IPC shm and shmat file. |
@@ -286,16 +313,11 @@ void ima_counts_get(struct file *file) | |||
286 | 313 | ||
287 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 314 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
288 | return; | 315 | return; |
289 | iint = ima_iint_find_insert_get(inode); | 316 | iint = ima_iint_find_get(inode); |
290 | if (!iint) | 317 | if (!iint) |
291 | return; | 318 | return; |
292 | mutex_lock(&iint->mutex); | 319 | mutex_lock(&iint->mutex); |
293 | iint->opencount++; | 320 | ima_inc_counts(iint, file->f_mode); |
294 | if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
295 | iint->readcount++; | ||
296 | |||
297 | if (file->f_mode & FMODE_WRITE) | ||
298 | iint->writecount++; | ||
299 | mutex_unlock(&iint->mutex); | 321 | mutex_unlock(&iint->mutex); |
300 | 322 | ||
301 | kref_put(&iint->refcount, iint_free); | 323 | kref_put(&iint->refcount, iint_free); |