diff options
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r-- | security/integrity/ima/ima_main.c | 184 |
1 files changed, 94 insertions, 90 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e662b89d407..203de979d30 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 | */ | ||
98 | enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; | ||
99 | static 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 | */ | ||
121 | static 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; | ||
168 | out: | 127 | out: |
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 | */ |
178 | static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode, | 145 | static 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) || | 164 | static 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 | |||
179 | static 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 | |||
192 | static 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 | */ |
213 | void ima_file_free(struct file *file) | 208 | void 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 | ||
230 | static int process_measurement(struct file *file, const unsigned char *filename, | 225 | static 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; | ||
238 | retry: | ||
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); |
251 | out: | 256 | out: |
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 | ||