Commit 5459c164f0591ee75ed0203bb8f3817f25948e2f
Committed by
Linus Torvalds
1 parent
78ecba0812
Exists in
master
and in
20 other branches
security: protect legacy applications from executing with insufficient privilege
When cap_bset suppresses some of the forced (fP) capabilities of a file, it is generally only safe to execute the program if it understands how to recognize it doesn't have enough privilege to work correctly. For legacy applications (fE!=0), which have no non-destructive way to determine that they are missing privilege, we fail to execute (EPERM) any executable that requires fP capabilities, but would otherwise get pP' < fP. This is a fail-safe permission check. For some discussion of why it is problematic for (legacy) privileged applications to run with less than the set of capabilities requested for them, see: http://userweb.kernel.org/~morgan/sendmail-capabilities-war-story.html With this iteration of this support, we do not include setuid-0 based privilege protection from the bounding set. That is, the admin can still (ab)use the bounding set to suppress the privileges of a setuid-0 program. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: cleanup] Signed-off-by: Andrew G. Morgan <morgan@kernel.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 2 changed files with 60 additions and 50 deletions Side-by-side Diff
include/linux/binfmts.h
security/commoncap.c
... | ... | @@ -162,8 +162,7 @@ |
162 | 162 | |
163 | 163 | static inline void bprm_clear_caps(struct linux_binprm *bprm) |
164 | 164 | { |
165 | - cap_clear(bprm->cap_inheritable); | |
166 | - cap_clear(bprm->cap_permitted); | |
165 | + cap_clear(bprm->cap_post_exec_permitted); | |
167 | 166 | bprm->cap_effective = false; |
168 | 167 | } |
169 | 168 | |
... | ... | @@ -198,6 +197,7 @@ |
198 | 197 | { |
199 | 198 | __u32 magic_etc; |
200 | 199 | unsigned tocopy, i; |
200 | + int ret; | |
201 | 201 | |
202 | 202 | if (size < sizeof(magic_etc)) |
203 | 203 | return -EINVAL; |
204 | 204 | |
205 | 205 | |
... | ... | @@ -225,19 +225,40 @@ |
225 | 225 | bprm->cap_effective = false; |
226 | 226 | } |
227 | 227 | |
228 | - for (i = 0; i < tocopy; ++i) { | |
229 | - bprm->cap_permitted.cap[i] = | |
230 | - le32_to_cpu(caps->data[i].permitted); | |
231 | - bprm->cap_inheritable.cap[i] = | |
232 | - le32_to_cpu(caps->data[i].inheritable); | |
228 | + ret = 0; | |
229 | + | |
230 | + CAP_FOR_EACH_U32(i) { | |
231 | + __u32 value_cpu; | |
232 | + | |
233 | + if (i >= tocopy) { | |
234 | + /* | |
235 | + * Legacy capability sets have no upper bits | |
236 | + */ | |
237 | + bprm->cap_post_exec_permitted.cap[i] = 0; | |
238 | + continue; | |
239 | + } | |
240 | + /* | |
241 | + * pP' = (X & fP) | (pI & fI) | |
242 | + */ | |
243 | + value_cpu = le32_to_cpu(caps->data[i].permitted); | |
244 | + bprm->cap_post_exec_permitted.cap[i] = | |
245 | + (current->cap_bset.cap[i] & value_cpu) | | |
246 | + (current->cap_inheritable.cap[i] & | |
247 | + le32_to_cpu(caps->data[i].inheritable)); | |
248 | + if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) { | |
249 | + /* | |
250 | + * insufficient to execute correctly | |
251 | + */ | |
252 | + ret = -EPERM; | |
253 | + } | |
233 | 254 | } |
234 | - while (i < VFS_CAP_U32) { | |
235 | - bprm->cap_permitted.cap[i] = 0; | |
236 | - bprm->cap_inheritable.cap[i] = 0; | |
237 | - i++; | |
238 | - } | |
239 | 255 | |
240 | - return 0; | |
256 | + /* | |
257 | + * For legacy apps, with no internal support for recognizing they | |
258 | + * do not have enough capabilities, we return an error if they are | |
259 | + * missing some "forced" (aka file-permitted) capabilities. | |
260 | + */ | |
261 | + return bprm->cap_effective ? ret : 0; | |
241 | 262 | } |
242 | 263 | |
243 | 264 | /* Locate any VFS capabilities: */ |
244 | 265 | |
... | ... | @@ -269,9 +290,9 @@ |
269 | 290 | goto out; |
270 | 291 | |
271 | 292 | rc = cap_from_disk(&vcaps, bprm, rc); |
272 | - if (rc) | |
293 | + if (rc == -EINVAL) | |
273 | 294 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
274 | - __func__, rc, bprm->filename); | |
295 | + __func__, rc, bprm->filename); | |
275 | 296 | |
276 | 297 | out: |
277 | 298 | dput(dentry); |
278 | 299 | |
279 | 300 | |
280 | 301 | |
... | ... | @@ -304,25 +325,24 @@ |
304 | 325 | int ret; |
305 | 326 | |
306 | 327 | ret = get_file_caps(bprm); |
307 | - if (ret) | |
308 | - printk(KERN_NOTICE "%s: get_file_caps returned %d for %s\n", | |
309 | - __func__, ret, bprm->filename); | |
310 | 328 | |
311 | - /* To support inheritance of root-permissions and suid-root | |
312 | - * executables under compatibility mode, we raise all three | |
313 | - * capability sets for the file. | |
314 | - * | |
315 | - * If only the real uid is 0, we only raise the inheritable | |
316 | - * and permitted sets of the executable file. | |
317 | - */ | |
318 | - | |
319 | - if (!issecure (SECURE_NOROOT)) { | |
329 | + if (!issecure(SECURE_NOROOT)) { | |
330 | + /* | |
331 | + * To support inheritance of root-permissions and suid-root | |
332 | + * executables under compatibility mode, we override the | |
333 | + * capability sets for the file. | |
334 | + * | |
335 | + * If only the real uid is 0, we do not set the effective | |
336 | + * bit. | |
337 | + */ | |
320 | 338 | if (bprm->e_uid == 0 || current->uid == 0) { |
321 | - cap_set_full (bprm->cap_inheritable); | |
322 | - cap_set_full (bprm->cap_permitted); | |
339 | + /* pP' = (cap_bset & ~0) | (pI & ~0) */ | |
340 | + bprm->cap_post_exec_permitted = cap_combine( | |
341 | + current->cap_bset, current->cap_inheritable | |
342 | + ); | |
343 | + bprm->cap_effective = (bprm->e_uid == 0); | |
344 | + ret = 0; | |
323 | 345 | } |
324 | - if (bprm->e_uid == 0) | |
325 | - bprm->cap_effective = true; | |
326 | 346 | } |
327 | 347 | |
328 | 348 | return ret; |
329 | 349 | |
... | ... | @@ -330,17 +350,9 @@ |
330 | 350 | |
331 | 351 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) |
332 | 352 | { |
333 | - /* Derived from fs/exec.c:compute_creds. */ | |
334 | - kernel_cap_t new_permitted, working; | |
335 | - | |
336 | - new_permitted = cap_intersect(bprm->cap_permitted, | |
337 | - current->cap_bset); | |
338 | - working = cap_intersect(bprm->cap_inheritable, | |
339 | - current->cap_inheritable); | |
340 | - new_permitted = cap_combine(new_permitted, working); | |
341 | - | |
342 | 353 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || |
343 | - !cap_issubset (new_permitted, current->cap_permitted)) { | |
354 | + !cap_issubset(bprm->cap_post_exec_permitted, | |
355 | + current->cap_permitted)) { | |
344 | 356 | set_dumpable(current->mm, suid_dumpable); |
345 | 357 | current->pdeath_signal = 0; |
346 | 358 | |
... | ... | @@ -350,9 +362,9 @@ |
350 | 362 | bprm->e_gid = current->gid; |
351 | 363 | } |
352 | 364 | if (cap_limit_ptraced_target()) { |
353 | - new_permitted = | |
354 | - cap_intersect(new_permitted, | |
355 | - current->cap_permitted); | |
365 | + bprm->cap_post_exec_permitted = cap_intersect( | |
366 | + bprm->cap_post_exec_permitted, | |
367 | + current->cap_permitted); | |
356 | 368 | } |
357 | 369 | } |
358 | 370 | } |
359 | 371 | |
... | ... | @@ -364,9 +376,9 @@ |
364 | 376 | * in the init_task struct. Thus we skip the usual |
365 | 377 | * capability rules */ |
366 | 378 | if (!is_global_init(current)) { |
367 | - current->cap_permitted = new_permitted; | |
379 | + current->cap_permitted = bprm->cap_post_exec_permitted; | |
368 | 380 | if (bprm->cap_effective) |
369 | - current->cap_effective = new_permitted; | |
381 | + current->cap_effective = bprm->cap_post_exec_permitted; | |
370 | 382 | else |
371 | 383 | cap_clear(current->cap_effective); |
372 | 384 | } |
... | ... | @@ -381,9 +393,7 @@ |
381 | 393 | if (current->uid != 0) { |
382 | 394 | if (bprm->cap_effective) |
383 | 395 | return 1; |
384 | - if (!cap_isclear(bprm->cap_permitted)) | |
385 | - return 1; | |
386 | - if (!cap_isclear(bprm->cap_inheritable)) | |
396 | + if (!cap_isclear(bprm->cap_post_exec_permitted)) | |
387 | 397 | return 1; |
388 | 398 | } |
389 | 399 |