diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2008-07-24 00:28:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:22 -0400 |
commit | 5459c164f0591ee75ed0203bb8f3817f25948e2f (patch) | |
tree | 7b17a0cbadfc487d7311b7f5a41779ff33d6fe7f | |
parent | 78ecba081224a2db5876b6b81cfed0b78f58adc7 (diff) |
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>
-rw-r--r-- | include/linux/binfmts.h | 2 | ||||
-rw-r--r-- | security/commoncap.c | 108 |
2 files changed, 60 insertions, 50 deletions
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index ee0ed48e8348..826f62350805 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h | |||
@@ -38,7 +38,7 @@ struct linux_binprm{ | |||
38 | misc_bang:1; | 38 | misc_bang:1; |
39 | struct file * file; | 39 | struct file * file; |
40 | int e_uid, e_gid; | 40 | int e_uid, e_gid; |
41 | kernel_cap_t cap_inheritable, cap_permitted; | 41 | kernel_cap_t cap_post_exec_permitted; |
42 | bool cap_effective; | 42 | bool cap_effective; |
43 | void *security; | 43 | void *security; |
44 | int argc, envc; | 44 | int argc, envc; |
diff --git a/security/commoncap.c b/security/commoncap.c index 0b6537a3672d..4afbece37a08 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -162,8 +162,7 @@ void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, | |||
162 | 162 | ||
163 | static inline void bprm_clear_caps(struct linux_binprm *bprm) | 163 | static inline void bprm_clear_caps(struct linux_binprm *bprm) |
164 | { | 164 | { |
165 | cap_clear(bprm->cap_inheritable); | 165 | cap_clear(bprm->cap_post_exec_permitted); |
166 | cap_clear(bprm->cap_permitted); | ||
167 | bprm->cap_effective = false; | 166 | bprm->cap_effective = false; |
168 | } | 167 | } |
169 | 168 | ||
@@ -198,6 +197,7 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, | |||
198 | { | 197 | { |
199 | __u32 magic_etc; | 198 | __u32 magic_etc; |
200 | unsigned tocopy, i; | 199 | unsigned tocopy, i; |
200 | int ret; | ||
201 | 201 | ||
202 | if (size < sizeof(magic_etc)) | 202 | if (size < sizeof(magic_etc)) |
203 | return -EINVAL; | 203 | return -EINVAL; |
@@ -225,19 +225,40 @@ static inline int cap_from_disk(struct vfs_cap_data *caps, | |||
225 | bprm->cap_effective = false; | 225 | bprm->cap_effective = false; |
226 | } | 226 | } |
227 | 227 | ||
228 | for (i = 0; i < tocopy; ++i) { | 228 | ret = 0; |
229 | bprm->cap_permitted.cap[i] = | 229 | |
230 | le32_to_cpu(caps->data[i].permitted); | 230 | CAP_FOR_EACH_U32(i) { |
231 | bprm->cap_inheritable.cap[i] = | 231 | __u32 value_cpu; |
232 | le32_to_cpu(caps->data[i].inheritable); | 232 | |
233 | } | 233 | if (i >= tocopy) { |
234 | while (i < VFS_CAP_U32) { | 234 | /* |
235 | bprm->cap_permitted.cap[i] = 0; | 235 | * Legacy capability sets have no upper bits |
236 | bprm->cap_inheritable.cap[i] = 0; | 236 | */ |
237 | i++; | 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 | } | ||
238 | } | 254 | } |
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 | /* Locate any VFS capabilities: */ | 264 | /* Locate any VFS capabilities: */ |
@@ -269,9 +290,9 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
269 | goto out; | 290 | goto out; |
270 | 291 | ||
271 | rc = cap_from_disk(&vcaps, bprm, rc); | 292 | rc = cap_from_disk(&vcaps, bprm, rc); |
272 | if (rc) | 293 | if (rc == -EINVAL) |
273 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | 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 | out: | 297 | out: |
277 | dput(dentry); | 298 | dput(dentry); |
@@ -304,25 +325,24 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
304 | int ret; | 325 | int ret; |
305 | 326 | ||
306 | ret = get_file_caps(bprm); | 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 | |||
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 | 328 | ||
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 | if (bprm->e_uid == 0 || current->uid == 0) { | 338 | if (bprm->e_uid == 0 || current->uid == 0) { |
321 | cap_set_full (bprm->cap_inheritable); | 339 | /* pP' = (cap_bset & ~0) | (pI & ~0) */ |
322 | cap_set_full (bprm->cap_permitted); | 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 | return ret; | 348 | return ret; |
@@ -330,17 +350,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
330 | 350 | ||
331 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 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 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || | 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 | set_dumpable(current->mm, suid_dumpable); | 356 | set_dumpable(current->mm, suid_dumpable); |
345 | current->pdeath_signal = 0; | 357 | current->pdeath_signal = 0; |
346 | 358 | ||
@@ -350,9 +362,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
350 | bprm->e_gid = current->gid; | 362 | bprm->e_gid = current->gid; |
351 | } | 363 | } |
352 | if (cap_limit_ptraced_target()) { | 364 | if (cap_limit_ptraced_target()) { |
353 | new_permitted = | 365 | bprm->cap_post_exec_permitted = cap_intersect( |
354 | cap_intersect(new_permitted, | 366 | bprm->cap_post_exec_permitted, |
355 | current->cap_permitted); | 367 | current->cap_permitted); |
356 | } | 368 | } |
357 | } | 369 | } |
358 | } | 370 | } |
@@ -364,9 +376,9 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
364 | * in the init_task struct. Thus we skip the usual | 376 | * in the init_task struct. Thus we skip the usual |
365 | * capability rules */ | 377 | * capability rules */ |
366 | if (!is_global_init(current)) { | 378 | if (!is_global_init(current)) { |
367 | current->cap_permitted = new_permitted; | 379 | current->cap_permitted = bprm->cap_post_exec_permitted; |
368 | if (bprm->cap_effective) | 380 | if (bprm->cap_effective) |
369 | current->cap_effective = new_permitted; | 381 | current->cap_effective = bprm->cap_post_exec_permitted; |
370 | else | 382 | else |
371 | cap_clear(current->cap_effective); | 383 | cap_clear(current->cap_effective); |
372 | } | 384 | } |
@@ -381,9 +393,7 @@ int cap_bprm_secureexec (struct linux_binprm *bprm) | |||
381 | if (current->uid != 0) { | 393 | if (current->uid != 0) { |
382 | if (bprm->cap_effective) | 394 | if (bprm->cap_effective) |
383 | return 1; | 395 | return 1; |
384 | if (!cap_isclear(bprm->cap_permitted)) | 396 | if (!cap_isclear(bprm->cap_post_exec_permitted)) |
385 | return 1; | ||
386 | if (!cap_isclear(bprm->cap_inheritable)) | ||
387 | return 1; | 397 | return 1; |
388 | } | 398 | } |
389 | 399 | ||