aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-10-26 14:37:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-26 14:37:48 -0400
commitf9ba5375a8aae4aeea6be15df77e24707a429812 (patch)
treec6388d7e40f0f6a70d7ba6a4d4aeaa0d1f5591f6 /security
parent45352bbf48e95078b4acd20774f49e72676e1e0f (diff)
parentbade72d607c4eb1b1d6c7852c493b75f065a56b5 (diff)
Merge branch 'ima-memory-use-fixes'
* ima-memory-use-fixes: IMA: fix the ToMToU logic IMA: explicit IMA i_flag to remove global lock on inode_delete IMA: drop refcnt from ima_iint_cache since it isn't needed IMA: only allocate iint when needed IMA: move read counter into struct inode IMA: use i_writecount rather than a private counter IMA: use inode->i_lock to protect read and write counters IMA: convert internal flags from long to char IMA: use unsigned int instead of long for counters IMA: drop the inode opencount since it isn't needed for operation IMA: use rbtree instead of radix tree for inode information cache
Diffstat (limited to 'security')
-rw-r--r--security/integrity/ima/ima.h18
-rw-r--r--security/integrity/ima/ima_api.c2
-rw-r--r--security/integrity/ima/ima_iint.c158
-rw-r--r--security/integrity/ima/ima_main.c184
-rw-r--r--security/security.c10
5 files changed, 195 insertions, 177 deletions
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 3fbcd1dda0ef..ac79032bdf23 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -70,6 +70,7 @@ int ima_init(void);
70void ima_cleanup(void); 70void ima_cleanup(void);
71int ima_fs_init(void); 71int ima_fs_init(void);
72void ima_fs_cleanup(void); 72void ima_fs_cleanup(void);
73int ima_inode_alloc(struct inode *inode);
73int ima_add_template_entry(struct ima_template_entry *entry, int violation, 74int ima_add_template_entry(struct ima_template_entry *entry, int violation,
74 const char *op, struct inode *inode); 75 const char *op, struct inode *inode);
75int ima_calc_hash(struct file *file, char *digest); 76int ima_calc_hash(struct file *file, char *digest);
@@ -96,19 +97,16 @@ static inline unsigned long ima_hash_key(u8 *digest)
96} 97}
97 98
98/* iint cache flags */ 99/* iint cache flags */
99#define IMA_MEASURED 1 100#define IMA_MEASURED 0x01
100 101
101/* integrity data associated with an inode */ 102/* integrity data associated with an inode */
102struct ima_iint_cache { 103struct ima_iint_cache {
104 struct rb_node rb_node; /* rooted in ima_iint_tree */
105 struct inode *inode; /* back pointer to inode in question */
103 u64 version; /* track inode changes */ 106 u64 version; /* track inode changes */
104 unsigned long flags; 107 unsigned char flags;
105 u8 digest[IMA_DIGEST_SIZE]; 108 u8 digest[IMA_DIGEST_SIZE];
106 struct mutex mutex; /* protects: version, flags, digest */ 109 struct mutex mutex; /* protects: version, flags, digest */
107 long readcount; /* measured files readcount */
108 long writecount; /* measured files writecount */
109 long opencount; /* opens reference count */
110 struct kref refcount; /* ima_iint_cache reference count */
111 struct rcu_head rcu;
112}; 110};
113 111
114/* LIM API function definitions */ 112/* LIM API function definitions */
@@ -122,13 +120,11 @@ int ima_store_template(struct ima_template_entry *entry, int violation,
122void ima_template_show(struct seq_file *m, void *e, 120void ima_template_show(struct seq_file *m, void *e,
123 enum ima_show_type show); 121 enum ima_show_type show);
124 122
125/* radix tree calls to lookup, insert, delete 123/* rbtree tree calls to lookup, insert, delete
126 * integrity data associated with an inode. 124 * integrity data associated with an inode.
127 */ 125 */
128struct ima_iint_cache *ima_iint_insert(struct inode *inode); 126struct ima_iint_cache *ima_iint_insert(struct inode *inode);
129struct ima_iint_cache *ima_iint_find_get(struct inode *inode); 127struct ima_iint_cache *ima_iint_find(struct inode *inode);
130void iint_free(struct kref *kref);
131void iint_rcu_free(struct rcu_head *rcu);
132 128
133/* IMA policy related functions */ 129/* IMA policy related functions */
134enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; 130enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 52015d098fdf..d3963de6003d 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -116,7 +116,7 @@ int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
116{ 116{
117 int must_measure; 117 int must_measure;
118 118
119 if (iint->flags & IMA_MEASURED) 119 if (iint && iint->flags & IMA_MEASURED)
120 return 1; 120 return 1;
121 121
122 must_measure = ima_match_policy(inode, function, mask); 122 must_measure = ima_match_policy(inode, function, mask);
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index afba4aef812f..c442e47b6785 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -12,98 +12,119 @@
12 * File: ima_iint.c 12 * File: ima_iint.c
13 * - implements the IMA hooks: ima_inode_alloc, ima_inode_free 13 * - implements the IMA hooks: ima_inode_alloc, ima_inode_free
14 * - cache integrity information associated with an inode 14 * - cache integrity information associated with an inode
15 * using a radix tree. 15 * using a rbtree tree.
16 */ 16 */
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/spinlock.h> 19#include <linux/spinlock.h>
20#include <linux/radix-tree.h> 20#include <linux/rbtree.h>
21#include "ima.h" 21#include "ima.h"
22 22
23RADIX_TREE(ima_iint_store, GFP_ATOMIC); 23static struct rb_root ima_iint_tree = RB_ROOT;
24DEFINE_SPINLOCK(ima_iint_lock); 24static DEFINE_SPINLOCK(ima_iint_lock);
25static struct kmem_cache *iint_cache __read_mostly; 25static struct kmem_cache *iint_cache __read_mostly;
26 26
27int iint_initialized = 0; 27int iint_initialized = 0;
28 28
29/* ima_iint_find_get - return the iint associated with an inode 29/*
30 * 30 * __ima_iint_find - return the iint associated with an inode
31 * ima_iint_find_get gets a reference to the iint. Caller must
32 * remember to put the iint reference.
33 */ 31 */
34struct ima_iint_cache *ima_iint_find_get(struct inode *inode) 32static struct ima_iint_cache *__ima_iint_find(struct inode *inode)
35{ 33{
36 struct ima_iint_cache *iint; 34 struct ima_iint_cache *iint;
35 struct rb_node *n = ima_iint_tree.rb_node;
36
37 assert_spin_locked(&ima_iint_lock);
38
39 while (n) {
40 iint = rb_entry(n, struct ima_iint_cache, rb_node);
41
42 if (inode < iint->inode)
43 n = n->rb_left;
44 else if (inode > iint->inode)
45 n = n->rb_right;
46 else
47 break;
48 }
49 if (!n)
50 return NULL;
37 51
38 rcu_read_lock();
39 iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
40 if (!iint)
41 goto out;
42 kref_get(&iint->refcount);
43out:
44 rcu_read_unlock();
45 return iint; 52 return iint;
46} 53}
47 54
48/** 55/*
49 * ima_inode_alloc - allocate an iint associated with an inode 56 * ima_iint_find - return the iint associated with an inode
50 * @inode: pointer to the inode
51 */ 57 */
52int ima_inode_alloc(struct inode *inode) 58struct ima_iint_cache *ima_iint_find(struct inode *inode)
53{ 59{
54 struct ima_iint_cache *iint = NULL; 60 struct ima_iint_cache *iint;
55 int rc = 0;
56
57 iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
58 if (!iint)
59 return -ENOMEM;
60 61
61 rc = radix_tree_preload(GFP_NOFS); 62 if (!IS_IMA(inode))
62 if (rc < 0) 63 return NULL;
63 goto out;
64 64
65 spin_lock(&ima_iint_lock); 65 spin_lock(&ima_iint_lock);
66 rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint); 66 iint = __ima_iint_find(inode);
67 spin_unlock(&ima_iint_lock); 67 spin_unlock(&ima_iint_lock);
68 radix_tree_preload_end();
69out:
70 if (rc < 0)
71 kmem_cache_free(iint_cache, iint);
72 68
73 return rc; 69 return iint;
74} 70}
75 71
76/* iint_free - called when the iint refcount goes to zero */ 72static void iint_free(struct ima_iint_cache *iint)
77void iint_free(struct kref *kref)
78{ 73{
79 struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
80 refcount);
81 iint->version = 0; 74 iint->version = 0;
82 iint->flags = 0UL; 75 iint->flags = 0UL;
83 if (iint->readcount != 0) {
84 printk(KERN_INFO "%s: readcount: %ld\n", __func__,
85 iint->readcount);
86 iint->readcount = 0;
87 }
88 if (iint->writecount != 0) {
89 printk(KERN_INFO "%s: writecount: %ld\n", __func__,
90 iint->writecount);
91 iint->writecount = 0;
92 }
93 if (iint->opencount != 0) {
94 printk(KERN_INFO "%s: opencount: %ld\n", __func__,
95 iint->opencount);
96 iint->opencount = 0;
97 }
98 kref_init(&iint->refcount);
99 kmem_cache_free(iint_cache, iint); 76 kmem_cache_free(iint_cache, iint);
100} 77}
101 78
102void iint_rcu_free(struct rcu_head *rcu_head) 79/**
80 * ima_inode_alloc - allocate an iint associated with an inode
81 * @inode: pointer to the inode
82 */
83int ima_inode_alloc(struct inode *inode)
103{ 84{
104 struct ima_iint_cache *iint = container_of(rcu_head, 85 struct rb_node **p;
105 struct ima_iint_cache, rcu); 86 struct rb_node *new_node, *parent = NULL;
106 kref_put(&iint->refcount, iint_free); 87 struct ima_iint_cache *new_iint, *test_iint;
88 int rc;
89
90 new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
91 if (!new_iint)
92 return -ENOMEM;
93
94 new_iint->inode = inode;
95 new_node = &new_iint->rb_node;
96
97 mutex_lock(&inode->i_mutex); /* i_flags */
98 spin_lock(&ima_iint_lock);
99
100 p = &ima_iint_tree.rb_node;
101 while (*p) {
102 parent = *p;
103 test_iint = rb_entry(parent, struct ima_iint_cache, rb_node);
104
105 rc = -EEXIST;
106 if (inode < test_iint->inode)
107 p = &(*p)->rb_left;
108 else if (inode > test_iint->inode)
109 p = &(*p)->rb_right;
110 else
111 goto out_err;
112 }
113
114 inode->i_flags |= S_IMA;
115 rb_link_node(new_node, parent, p);
116 rb_insert_color(new_node, &ima_iint_tree);
117
118 spin_unlock(&ima_iint_lock);
119 mutex_unlock(&inode->i_mutex); /* i_flags */
120
121 return 0;
122out_err:
123 spin_unlock(&ima_iint_lock);
124 mutex_unlock(&inode->i_mutex); /* i_flags */
125 iint_free(new_iint);
126
127 return rc;
107} 128}
108 129
109/** 130/**
@@ -116,11 +137,20 @@ void ima_inode_free(struct inode *inode)
116{ 137{
117 struct ima_iint_cache *iint; 138 struct ima_iint_cache *iint;
118 139
140 if (inode->i_readcount)
141 printk(KERN_INFO "%s: readcount: %u\n", __func__, inode->i_readcount);
142
143 inode->i_readcount = 0;
144
145 if (!IS_IMA(inode))
146 return;
147
119 spin_lock(&ima_iint_lock); 148 spin_lock(&ima_iint_lock);
120 iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); 149 iint = __ima_iint_find(inode);
150 rb_erase(&iint->rb_node, &ima_iint_tree);
121 spin_unlock(&ima_iint_lock); 151 spin_unlock(&ima_iint_lock);
122 if (iint) 152
123 call_rcu(&iint->rcu, iint_rcu_free); 153 iint_free(iint);
124} 154}
125 155
126static void init_once(void *foo) 156static void init_once(void *foo)
@@ -131,10 +161,6 @@ static void init_once(void *foo)
131 iint->version = 0; 161 iint->version = 0;
132 iint->flags = 0UL; 162 iint->flags = 0UL;
133 mutex_init(&iint->mutex); 163 mutex_init(&iint->mutex);
134 iint->readcount = 0;
135 iint->writecount = 0;
136 iint->opencount = 0;
137 kref_init(&iint->refcount);
138} 164}
139 165
140static int __init ima_iintcache_init(void) 166static int __init ima_iintcache_init(void)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index e662b89d4079..203de979d305 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -85,50 +85,6 @@ out:
85 return found; 85 return found;
86} 86}
87 87
88/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
89 *
90 * When opening a file for read, if the file is already open for write,
91 * the file could change, resulting in a file measurement error.
92 *
93 * Opening a file for write, if the file is already open for read, results
94 * in a time of measure, time of use (ToMToU) error.
95 *
96 * In either case invalidate the PCR.
97 */
98enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
99static void ima_read_write_check(enum iint_pcr_error error,
100 struct ima_iint_cache *iint,
101 struct inode *inode,
102 const unsigned char *filename)
103{
104 switch (error) {
105 case TOMTOU:
106 if (iint->readcount > 0)
107 ima_add_violation(inode, filename, "invalid_pcr",
108 "ToMToU");
109 break;
110 case OPEN_WRITERS:
111 if (iint->writecount > 0)
112 ima_add_violation(inode, filename, "invalid_pcr",
113 "open_writers");
114 break;
115 }
116}
117
118/*
119 * Update the counts given an fmode_t
120 */
121static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode)
122{
123 BUG_ON(!mutex_is_locked(&iint->mutex));
124
125 iint->opencount++;
126 if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
127 iint->readcount++;
128 if (mode & FMODE_WRITE)
129 iint->writecount++;
130}
131
132/* 88/*
133 * ima_counts_get - increment file counts 89 * ima_counts_get - increment file counts
134 * 90 *
@@ -145,62 +101,101 @@ void ima_counts_get(struct file *file)
145 struct dentry *dentry = file->f_path.dentry; 101 struct dentry *dentry = file->f_path.dentry;
146 struct inode *inode = dentry->d_inode; 102 struct inode *inode = dentry->d_inode;
147 fmode_t mode = file->f_mode; 103 fmode_t mode = file->f_mode;
148 struct ima_iint_cache *iint;
149 int rc; 104 int rc;
105 bool send_tomtou = false, send_writers = false;
150 106
151 if (!iint_initialized || !S_ISREG(inode->i_mode)) 107 if (!S_ISREG(inode->i_mode))
152 return; 108 return;
153 iint = ima_iint_find_get(inode); 109
154 if (!iint) 110 spin_lock(&inode->i_lock);
155 return; 111
156 mutex_lock(&iint->mutex);
157 if (!ima_initialized) 112 if (!ima_initialized)
158 goto out; 113 goto out;
159 rc = ima_must_measure(iint, inode, MAY_READ, FILE_CHECK);
160 if (rc < 0)
161 goto out;
162 114
163 if (mode & FMODE_WRITE) { 115 if (mode & FMODE_WRITE) {
164 ima_read_write_check(TOMTOU, iint, inode, dentry->d_name.name); 116 if (inode->i_readcount && IS_IMA(inode))
117 send_tomtou = true;
165 goto out; 118 goto out;
166 } 119 }
167 ima_read_write_check(OPEN_WRITERS, iint, inode, dentry->d_name.name); 120
121 rc = ima_must_measure(NULL, inode, MAY_READ, FILE_CHECK);
122 if (rc < 0)
123 goto out;
124
125 if (atomic_read(&inode->i_writecount) > 0)
126 send_writers = true;
168out: 127out:
169 ima_inc_counts(iint, file->f_mode); 128 /* remember the vfs deals with i_writecount */
170 mutex_unlock(&iint->mutex); 129 if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
130 inode->i_readcount++;
171 131
172 kref_put(&iint->refcount, iint_free); 132 spin_unlock(&inode->i_lock);
133
134 if (send_tomtou)
135 ima_add_violation(inode, dentry->d_name.name, "invalid_pcr",
136 "ToMToU");
137 if (send_writers)
138 ima_add_violation(inode, dentry->d_name.name, "invalid_pcr",
139 "open_writers");
173} 140}
174 141
175/* 142/*
176 * Decrement ima counts 143 * Decrement ima counts
177 */ 144 */
178static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode, 145static void ima_dec_counts(struct inode *inode, struct file *file)
179 struct file *file)
180{ 146{
181 mode_t mode = file->f_mode; 147 mode_t mode = file->f_mode;
182 BUG_ON(!mutex_is_locked(&iint->mutex));
183 148
184 iint->opencount--; 149 assert_spin_locked(&inode->i_lock);
185 if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) 150
186 iint->readcount--; 151 if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
187 if (mode & FMODE_WRITE) { 152 if (unlikely(inode->i_readcount == 0)) {
188 iint->writecount--; 153 if (!ima_limit_imbalance(file)) {
189 if (iint->writecount == 0) { 154 printk(KERN_INFO "%s: open/free imbalance (r:%u)\n",
190 if (iint->version != inode->i_version) 155 __func__, inode->i_readcount);
191 iint->flags &= ~IMA_MEASURED; 156 dump_stack();
157 }
158 return;
192 } 159 }
160 inode->i_readcount--;
193 } 161 }
162}
194 163
195 if (((iint->opencount < 0) || 164static void ima_check_last_writer(struct ima_iint_cache *iint,
196 (iint->readcount < 0) || 165 struct inode *inode,
197 (iint->writecount < 0)) && 166 struct file *file)
198 !ima_limit_imbalance(file)) { 167{
199 printk(KERN_INFO "%s: open/free imbalance (r:%ld w:%ld o:%ld)\n", 168 mode_t mode = file->f_mode;
200 __func__, iint->readcount, iint->writecount, 169
201 iint->opencount); 170 BUG_ON(!mutex_is_locked(&iint->mutex));
202 dump_stack(); 171 assert_spin_locked(&inode->i_lock);
203 } 172
173 if (mode & FMODE_WRITE &&
174 atomic_read(&inode->i_writecount) == 1 &&
175 iint->version != inode->i_version)
176 iint->flags &= ~IMA_MEASURED;
177}
178
179static void ima_file_free_iint(struct ima_iint_cache *iint, struct inode *inode,
180 struct file *file)
181{
182 mutex_lock(&iint->mutex);
183 spin_lock(&inode->i_lock);
184
185 ima_dec_counts(inode, file);
186 ima_check_last_writer(iint, inode, file);
187
188 spin_unlock(&inode->i_lock);
189 mutex_unlock(&iint->mutex);
190}
191
192static void ima_file_free_noiint(struct inode *inode, struct file *file)
193{
194 spin_lock(&inode->i_lock);
195
196 ima_dec_counts(inode, file);
197
198 spin_unlock(&inode->i_lock);
204} 199}
205 200
206/** 201/**
@@ -208,7 +203,7 @@ static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode,
208 * @file: pointer to file structure being freed 203 * @file: pointer to file structure being freed
209 * 204 *
210 * Flag files that changed, based on i_version; 205 * Flag files that changed, based on i_version;
211 * and decrement the iint readcount/writecount. 206 * and decrement the i_readcount.
212 */ 207 */
213void ima_file_free(struct file *file) 208void ima_file_free(struct file *file)
214{ 209{
@@ -217,14 +212,14 @@ void ima_file_free(struct file *file)
217 212
218 if (!iint_initialized || !S_ISREG(inode->i_mode)) 213 if (!iint_initialized || !S_ISREG(inode->i_mode))
219 return; 214 return;
220 iint = ima_iint_find_get(inode);
221 if (!iint)
222 return;
223 215
224 mutex_lock(&iint->mutex); 216 iint = ima_iint_find(inode);
225 ima_dec_counts(iint, inode, file); 217
226 mutex_unlock(&iint->mutex); 218 if (iint)
227 kref_put(&iint->refcount, iint_free); 219 ima_file_free_iint(iint, inode, file);
220 else
221 ima_file_free_noiint(inode, file);
222
228} 223}
229 224
230static int process_measurement(struct file *file, const unsigned char *filename, 225static int process_measurement(struct file *file, const unsigned char *filename,
@@ -236,11 +231,21 @@ static int process_measurement(struct file *file, const unsigned char *filename,
236 231
237 if (!ima_initialized || !S_ISREG(inode->i_mode)) 232 if (!ima_initialized || !S_ISREG(inode->i_mode))
238 return 0; 233 return 0;
239 iint = ima_iint_find_get(inode); 234
240 if (!iint) 235 rc = ima_must_measure(NULL, inode, mask, function);
241 return -ENOMEM; 236 if (rc != 0)
237 return rc;
238retry:
239 iint = ima_iint_find(inode);
240 if (!iint) {
241 rc = ima_inode_alloc(inode);
242 if (!rc || rc == -EEXIST)
243 goto retry;
244 return rc;
245 }
242 246
243 mutex_lock(&iint->mutex); 247 mutex_lock(&iint->mutex);
248
244 rc = ima_must_measure(iint, inode, mask, function); 249 rc = ima_must_measure(iint, inode, mask, function);
245 if (rc != 0) 250 if (rc != 0)
246 goto out; 251 goto out;
@@ -250,7 +255,6 @@ static int process_measurement(struct file *file, const unsigned char *filename,
250 ima_store_measurement(iint, file, filename); 255 ima_store_measurement(iint, file, filename);
251out: 256out:
252 mutex_unlock(&iint->mutex); 257 mutex_unlock(&iint->mutex);
253 kref_put(&iint->refcount, iint_free);
254 return rc; 258 return rc;
255} 259}
256 260
diff --git a/security/security.c b/security/security.c
index b50f472061a4..3ef5e2a7a741 100644
--- a/security/security.c
+++ b/security/security.c
@@ -325,16 +325,8 @@ EXPORT_SYMBOL(security_sb_parse_opts_str);
325 325
326int security_inode_alloc(struct inode *inode) 326int security_inode_alloc(struct inode *inode)
327{ 327{
328 int ret;
329
330 inode->i_security = NULL; 328 inode->i_security = NULL;
331 ret = security_ops->inode_alloc_security(inode); 329 return security_ops->inode_alloc_security(inode);
332 if (ret)
333 return ret;
334 ret = ima_inode_alloc(inode);
335 if (ret)
336 security_inode_free(inode);
337 return ret;
338} 330}
339 331
340void security_inode_free(struct inode *inode) 332void security_inode_free(struct inode *inode)