diff options
author | Roberto Sassu <roberto.sassu@polito.it> | 2014-09-12 13:35:55 -0400 |
---|---|---|
committer | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2014-09-18 10:03:55 -0400 |
commit | f7a859ff7395c0ffe60f9563df5354473e5f9244 (patch) | |
tree | 76e56090f72f9c7a26ae337da4e6ac09e9a34fea /security/integrity | |
parent | a756024efea259282e65f3a00f512b094e805d76 (diff) |
ima: fix race condition on ima_rdwr_violation_check and process_measurement
This patch fixes a race condition between two functions that try to access
the same inode. Since the i_mutex lock is held and released separately
in the two functions, there may be the possibility that a violation is
not correctly detected.
Suppose there are two processes, A (reader) and B (writer), if the
following sequence happens:
A: ima_rdwr_violation_check()
B: ima_rdwr_violation_check()
B: process_measurement()
B: starts writing the inode
A: process_measurement()
the ToMToU violation (a reader may be accessing a content different from
that measured, due to a concurrent modification by a writer) will not be
detected. To avoid this issue, the violation check and the measurement
must be done atomically.
This patch fixes the problem by moving the violation check inside
process_measurement() when the i_mutex lock is held. Differently from
the old code, the violation check is executed also for the MMAP_CHECK
hook (other than for FILE_CHECK). This allows to detect ToMToU violations
that are possible because shared libraries can be opened for writing
while they are in use (according to the output of 'man mmap', the mmap()
flag MAP_DENYWRITE is ignored).
Changes in v5 (Roberto Sassu):
* get iint if action is not zero
* exit process_measurement() after the violation check if action is zero
* reverse order process_measurement() exit cleanup (Mimi)
Changes in v4 (Dmitry Kasatkin):
* iint allocation is done before calling ima_rdrw_violation_check()
(Suggested-by Mimi)
* do not check for violations if the policy does not contain 'measure'
rules (done by Roberto Sassu)
Changes in v3 (Dmitry Kasatkin):
* no violation checking for MMAP_CHECK function in this patch
* remove use of filename from violation
* removes checking if ima is enabled from ima_rdrw_violation_check
* slight style change
Suggested-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
Signed-off-by: Roberto Sassu <roberto.sassu@polito.it>
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/ima/ima_main.c | 54 |
1 files changed, 33 insertions, 21 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 2191b36ad1da..03bb52ecf490 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -77,21 +77,19 @@ __setup("ima_hash=", hash_setup); | |||
77 | * could result in a file measurement error. | 77 | * could result in a file measurement error. |
78 | * | 78 | * |
79 | */ | 79 | */ |
80 | static void ima_rdwr_violation_check(struct file *file) | 80 | static void ima_rdwr_violation_check(struct file *file, |
81 | struct integrity_iint_cache *iint, | ||
82 | char **pathbuf, | ||
83 | const char **pathname) | ||
81 | { | 84 | { |
82 | struct inode *inode = file_inode(file); | 85 | struct inode *inode = file_inode(file); |
83 | fmode_t mode = file->f_mode; | 86 | fmode_t mode = file->f_mode; |
84 | bool send_tomtou = false, send_writers = false; | 87 | bool send_tomtou = false, send_writers = false; |
85 | char *pathbuf = NULL; | ||
86 | const char *pathname; | ||
87 | |||
88 | if (!S_ISREG(inode->i_mode) || !(ima_policy_flag & IMA_MEASURE)) | ||
89 | return; | ||
90 | 88 | ||
91 | if (mode & FMODE_WRITE) { | 89 | if (mode & FMODE_WRITE) { |
92 | if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { | 90 | if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) { |
93 | struct integrity_iint_cache *iint; | 91 | if (!iint) |
94 | iint = integrity_iint_find(inode); | 92 | iint = integrity_iint_find(inode); |
95 | /* IMA_MEASURE is set from reader side */ | 93 | /* IMA_MEASURE is set from reader side */ |
96 | if (iint && (iint->flags & IMA_MEASURE)) | 94 | if (iint && (iint->flags & IMA_MEASURE)) |
97 | send_tomtou = true; | 95 | send_tomtou = true; |
@@ -105,14 +103,13 @@ static void ima_rdwr_violation_check(struct file *file) | |||
105 | if (!send_tomtou && !send_writers) | 103 | if (!send_tomtou && !send_writers) |
106 | return; | 104 | return; |
107 | 105 | ||
108 | pathname = ima_d_path(&file->f_path, &pathbuf); | 106 | *pathname = ima_d_path(&file->f_path, pathbuf); |
109 | 107 | ||
110 | if (send_tomtou) | 108 | if (send_tomtou) |
111 | ima_add_violation(file, pathname, "invalid_pcr", "ToMToU"); | 109 | ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU"); |
112 | if (send_writers) | 110 | if (send_writers) |
113 | ima_add_violation(file, pathname, | 111 | ima_add_violation(file, *pathname, |
114 | "invalid_pcr", "open_writers"); | 112 | "invalid_pcr", "open_writers"); |
115 | kfree(pathbuf); | ||
116 | } | 113 | } |
117 | 114 | ||
118 | static void ima_check_last_writer(struct integrity_iint_cache *iint, | 115 | static void ima_check_last_writer(struct integrity_iint_cache *iint, |
@@ -160,13 +157,14 @@ static int process_measurement(struct file *file, int mask, int function, | |||
160 | int opened) | 157 | int opened) |
161 | { | 158 | { |
162 | struct inode *inode = file_inode(file); | 159 | struct inode *inode = file_inode(file); |
163 | struct integrity_iint_cache *iint; | 160 | struct integrity_iint_cache *iint = NULL; |
164 | struct ima_template_desc *template_desc; | 161 | struct ima_template_desc *template_desc; |
165 | char *pathbuf = NULL; | 162 | char *pathbuf = NULL; |
166 | const char *pathname = NULL; | 163 | const char *pathname = NULL; |
167 | int rc = -ENOMEM, action, must_appraise; | 164 | int rc = -ENOMEM, action, must_appraise; |
168 | struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; | 165 | struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; |
169 | int xattr_len = 0; | 166 | int xattr_len = 0; |
167 | bool violation_check; | ||
170 | 168 | ||
171 | if (!ima_policy_flag || !S_ISREG(inode->i_mode)) | 169 | if (!ima_policy_flag || !S_ISREG(inode->i_mode)) |
172 | return 0; | 170 | return 0; |
@@ -176,7 +174,9 @@ static int process_measurement(struct file *file, int mask, int function, | |||
176 | * Included is the appraise submask. | 174 | * Included is the appraise submask. |
177 | */ | 175 | */ |
178 | action = ima_get_action(inode, mask, function); | 176 | action = ima_get_action(inode, mask, function); |
179 | if (!action) | 177 | violation_check = (function == FILE_CHECK && |
178 | (ima_policy_flag & IMA_MEASURE)); | ||
179 | if (!action && !violation_check) | ||
180 | return 0; | 180 | return 0; |
181 | 181 | ||
182 | must_appraise = action & IMA_APPRAISE; | 182 | must_appraise = action & IMA_APPRAISE; |
@@ -187,9 +187,19 @@ static int process_measurement(struct file *file, int mask, int function, | |||
187 | 187 | ||
188 | mutex_lock(&inode->i_mutex); | 188 | mutex_lock(&inode->i_mutex); |
189 | 189 | ||
190 | iint = integrity_inode_get(inode); | 190 | if (action) { |
191 | if (!iint) | 191 | iint = integrity_inode_get(inode); |
192 | goto out; | 192 | if (!iint) |
193 | goto out; | ||
194 | } | ||
195 | |||
196 | if (violation_check) { | ||
197 | ima_rdwr_violation_check(file, iint, &pathbuf, &pathname); | ||
198 | if (!action) { | ||
199 | rc = 0; | ||
200 | goto out_free; | ||
201 | } | ||
202 | } | ||
193 | 203 | ||
194 | /* Determine if already appraised/measured based on bitmask | 204 | /* Determine if already appraised/measured based on bitmask |
195 | * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, | 205 | * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, |
@@ -218,7 +228,8 @@ static int process_measurement(struct file *file, int mask, int function, | |||
218 | goto out_digsig; | 228 | goto out_digsig; |
219 | } | 229 | } |
220 | 230 | ||
221 | pathname = ima_d_path(&file->f_path, &pathbuf); | 231 | if (!pathname) /* ima_rdwr_violation possibly pre-fetched */ |
232 | pathname = ima_d_path(&file->f_path, &pathbuf); | ||
222 | 233 | ||
223 | if (action & IMA_MEASURE) | 234 | if (action & IMA_MEASURE) |
224 | ima_store_measurement(iint, file, pathname, | 235 | ima_store_measurement(iint, file, pathname, |
@@ -228,13 +239,15 @@ static int process_measurement(struct file *file, int mask, int function, | |||
228 | xattr_value, xattr_len, opened); | 239 | xattr_value, xattr_len, opened); |
229 | if (action & IMA_AUDIT) | 240 | if (action & IMA_AUDIT) |
230 | ima_audit_measurement(iint, pathname); | 241 | ima_audit_measurement(iint, pathname); |
231 | kfree(pathbuf); | 242 | |
232 | out_digsig: | 243 | out_digsig: |
233 | if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) | 244 | if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) |
234 | rc = -EACCES; | 245 | rc = -EACCES; |
246 | kfree(xattr_value); | ||
247 | out_free: | ||
248 | kfree(pathbuf); | ||
235 | out: | 249 | out: |
236 | mutex_unlock(&inode->i_mutex); | 250 | mutex_unlock(&inode->i_mutex); |
237 | kfree(xattr_value); | ||
238 | if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) | 251 | if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) |
239 | return -EACCES; | 252 | return -EACCES; |
240 | return 0; | 253 | return 0; |
@@ -288,7 +301,6 @@ int ima_bprm_check(struct linux_binprm *bprm) | |||
288 | */ | 301 | */ |
289 | int ima_file_check(struct file *file, int mask, int opened) | 302 | int ima_file_check(struct file *file, int mask, int opened) |
290 | { | 303 | { |
291 | ima_rdwr_violation_check(file); | ||
292 | return process_measurement(file, | 304 | return process_measurement(file, |
293 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | 305 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), |
294 | FILE_CHECK, opened); | 306 | FILE_CHECK, opened); |