diff options
Diffstat (limited to 'security/commoncap.c')
-rw-r--r-- | security/commoncap.c | 134 |
1 files changed, 82 insertions, 52 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index ea61bc73f6d3..5aba82679a0b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* Common capabilities, needed by capability.o and root_plug.o | 1 | /* Common capabilities, needed by capability.o and root_plug.o |
2 | * | 2 | * |
3 | * This program is free software; you can redistribute it and/or modify | 3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by | 4 | * it under the terms of the GNU General Public License as published by |
@@ -25,20 +25,6 @@ | |||
25 | #include <linux/mount.h> | 25 | #include <linux/mount.h> |
26 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
27 | 27 | ||
28 | #ifdef CONFIG_SECURITY_FILE_CAPABILITIES | ||
29 | /* | ||
30 | * Because of the reduced scope of CAP_SETPCAP when filesystem | ||
31 | * capabilities are in effect, it is safe to allow this capability to | ||
32 | * be available in the default configuration. | ||
33 | */ | ||
34 | # define CAP_INIT_BSET CAP_FULL_SET | ||
35 | #else /* ie. ndef CONFIG_SECURITY_FILE_CAPABILITIES */ | ||
36 | # define CAP_INIT_BSET CAP_INIT_EFF_SET | ||
37 | #endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */ | ||
38 | |||
39 | kernel_cap_t cap_bset = CAP_INIT_BSET; /* systemwide capability bound */ | ||
40 | EXPORT_SYMBOL(cap_bset); | ||
41 | |||
42 | /* Global security state */ | 28 | /* Global security state */ |
43 | 29 | ||
44 | unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ | 30 | unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */ |
@@ -93,9 +79,9 @@ int cap_capget (struct task_struct *target, kernel_cap_t *effective, | |||
93 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | 79 | kernel_cap_t *inheritable, kernel_cap_t *permitted) |
94 | { | 80 | { |
95 | /* Derived from kernel/capability.c:sys_capget. */ | 81 | /* Derived from kernel/capability.c:sys_capget. */ |
96 | *effective = cap_t (target->cap_effective); | 82 | *effective = target->cap_effective; |
97 | *inheritable = cap_t (target->cap_inheritable); | 83 | *inheritable = target->cap_inheritable; |
98 | *permitted = cap_t (target->cap_permitted); | 84 | *permitted = target->cap_permitted; |
99 | return 0; | 85 | return 0; |
100 | } | 86 | } |
101 | 87 | ||
@@ -140,6 +126,12 @@ int cap_capset_check (struct task_struct *target, kernel_cap_t *effective, | |||
140 | /* incapable of using this inheritable set */ | 126 | /* incapable of using this inheritable set */ |
141 | return -EPERM; | 127 | return -EPERM; |
142 | } | 128 | } |
129 | if (!cap_issubset(*inheritable, | ||
130 | cap_combine(target->cap_inheritable, | ||
131 | current->cap_bset))) { | ||
132 | /* no new pI capabilities outside bounding set */ | ||
133 | return -EPERM; | ||
134 | } | ||
143 | 135 | ||
144 | /* verify restrictions on target's new Permitted set */ | 136 | /* verify restrictions on target's new Permitted set */ |
145 | if (!cap_issubset (*permitted, | 137 | if (!cap_issubset (*permitted, |
@@ -198,28 +190,50 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
198 | } | 190 | } |
199 | 191 | ||
200 | static inline int cap_from_disk(struct vfs_cap_data *caps, | 192 | static inline int cap_from_disk(struct vfs_cap_data *caps, |
201 | struct linux_binprm *bprm, | 193 | struct linux_binprm *bprm, unsigned size) |
202 | int size) | ||
203 | { | 194 | { |
204 | __u32 magic_etc; | 195 | __u32 magic_etc; |
196 | unsigned tocopy, i; | ||
205 | 197 | ||
206 | if (size != XATTR_CAPS_SZ) | 198 | if (size < sizeof(magic_etc)) |
207 | return -EINVAL; | 199 | return -EINVAL; |
208 | 200 | ||
209 | magic_etc = le32_to_cpu(caps->magic_etc); | 201 | magic_etc = le32_to_cpu(caps->magic_etc); |
210 | 202 | ||
211 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { | 203 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { |
212 | case VFS_CAP_REVISION: | 204 | case VFS_CAP_REVISION_1: |
213 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | 205 | if (size != XATTR_CAPS_SZ_1) |
214 | bprm->cap_effective = true; | 206 | return -EINVAL; |
215 | else | 207 | tocopy = VFS_CAP_U32_1; |
216 | bprm->cap_effective = false; | 208 | break; |
217 | bprm->cap_permitted = to_cap_t(le32_to_cpu(caps->permitted)); | 209 | case VFS_CAP_REVISION_2: |
218 | bprm->cap_inheritable = to_cap_t(le32_to_cpu(caps->inheritable)); | 210 | if (size != XATTR_CAPS_SZ_2) |
219 | return 0; | 211 | return -EINVAL; |
212 | tocopy = VFS_CAP_U32_2; | ||
213 | break; | ||
220 | default: | 214 | default: |
221 | return -EINVAL; | 215 | return -EINVAL; |
222 | } | 216 | } |
217 | |||
218 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) { | ||
219 | bprm->cap_effective = true; | ||
220 | } else { | ||
221 | bprm->cap_effective = false; | ||
222 | } | ||
223 | |||
224 | for (i = 0; i < tocopy; ++i) { | ||
225 | bprm->cap_permitted.cap[i] = | ||
226 | le32_to_cpu(caps->data[i].permitted); | ||
227 | bprm->cap_inheritable.cap[i] = | ||
228 | le32_to_cpu(caps->data[i].inheritable); | ||
229 | } | ||
230 | while (i < VFS_CAP_U32) { | ||
231 | bprm->cap_permitted.cap[i] = 0; | ||
232 | bprm->cap_inheritable.cap[i] = 0; | ||
233 | i++; | ||
234 | } | ||
235 | |||
236 | return 0; | ||
223 | } | 237 | } |
224 | 238 | ||
225 | /* Locate any VFS capabilities: */ | 239 | /* Locate any VFS capabilities: */ |
@@ -227,7 +241,7 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
227 | { | 241 | { |
228 | struct dentry *dentry; | 242 | struct dentry *dentry; |
229 | int rc = 0; | 243 | int rc = 0; |
230 | struct vfs_cap_data incaps; | 244 | struct vfs_cap_data vcaps; |
231 | struct inode *inode; | 245 | struct inode *inode; |
232 | 246 | ||
233 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { | 247 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { |
@@ -240,14 +254,8 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
240 | if (!inode->i_op || !inode->i_op->getxattr) | 254 | if (!inode->i_op || !inode->i_op->getxattr) |
241 | goto out; | 255 | goto out; |
242 | 256 | ||
243 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0); | 257 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps, |
244 | if (rc > 0) { | 258 | XATTR_CAPS_SZ); |
245 | if (rc == XATTR_CAPS_SZ) | ||
246 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, | ||
247 | &incaps, XATTR_CAPS_SZ); | ||
248 | else | ||
249 | rc = -EINVAL; | ||
250 | } | ||
251 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { | 259 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { |
252 | /* no data, that's ok */ | 260 | /* no data, that's ok */ |
253 | rc = 0; | 261 | rc = 0; |
@@ -256,7 +264,7 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
256 | if (rc < 0) | 264 | if (rc < 0) |
257 | goto out; | 265 | goto out; |
258 | 266 | ||
259 | rc = cap_from_disk(&incaps, bprm, rc); | 267 | rc = cap_from_disk(&vcaps, bprm, rc); |
260 | if (rc) | 268 | if (rc) |
261 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | 269 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
262 | __FUNCTION__, rc, bprm->filename); | 270 | __FUNCTION__, rc, bprm->filename); |
@@ -321,10 +329,11 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
321 | /* Derived from fs/exec.c:compute_creds. */ | 329 | /* Derived from fs/exec.c:compute_creds. */ |
322 | kernel_cap_t new_permitted, working; | 330 | kernel_cap_t new_permitted, working; |
323 | 331 | ||
324 | new_permitted = cap_intersect (bprm->cap_permitted, cap_bset); | 332 | new_permitted = cap_intersect(bprm->cap_permitted, |
325 | working = cap_intersect (bprm->cap_inheritable, | 333 | current->cap_bset); |
334 | working = cap_intersect(bprm->cap_inheritable, | ||
326 | current->cap_inheritable); | 335 | current->cap_inheritable); |
327 | new_permitted = cap_combine (new_permitted, working); | 336 | new_permitted = cap_combine(new_permitted, working); |
328 | 337 | ||
329 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || | 338 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || |
330 | !cap_issubset (new_permitted, current->cap_permitted)) { | 339 | !cap_issubset (new_permitted, current->cap_permitted)) { |
@@ -351,8 +360,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
351 | * capability rules */ | 360 | * capability rules */ |
352 | if (!is_global_init(current)) { | 361 | if (!is_global_init(current)) { |
353 | current->cap_permitted = new_permitted; | 362 | current->cap_permitted = new_permitted; |
354 | current->cap_effective = bprm->cap_effective ? | 363 | if (bprm->cap_effective) |
355 | new_permitted : 0; | 364 | current->cap_effective = new_permitted; |
365 | else | ||
366 | cap_clear(current->cap_effective); | ||
356 | } | 367 | } |
357 | 368 | ||
358 | /* AUD: Audit candidate if current->cap_effective is set */ | 369 | /* AUD: Audit candidate if current->cap_effective is set */ |
@@ -474,13 +485,15 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, | |||
474 | 485 | ||
475 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { | 486 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { |
476 | if (old_fsuid == 0 && current->fsuid != 0) { | 487 | if (old_fsuid == 0 && current->fsuid != 0) { |
477 | cap_t (current->cap_effective) &= | 488 | current->cap_effective = |
478 | ~CAP_FS_MASK; | 489 | cap_drop_fs_set( |
490 | current->cap_effective); | ||
479 | } | 491 | } |
480 | if (old_fsuid != 0 && current->fsuid == 0) { | 492 | if (old_fsuid != 0 && current->fsuid == 0) { |
481 | cap_t (current->cap_effective) |= | 493 | current->cap_effective = |
482 | (cap_t (current->cap_permitted) & | 494 | cap_raise_fs_set( |
483 | CAP_FS_MASK); | 495 | current->cap_effective, |
496 | current->cap_permitted); | ||
484 | } | 497 | } |
485 | } | 498 | } |
486 | break; | 499 | break; |
@@ -561,6 +574,23 @@ int cap_task_kill(struct task_struct *p, struct siginfo *info, | |||
561 | 574 | ||
562 | return -EPERM; | 575 | return -EPERM; |
563 | } | 576 | } |
577 | |||
578 | /* | ||
579 | * called from kernel/sys.c for prctl(PR_CABSET_DROP) | ||
580 | * done without task_capability_lock() because it introduces | ||
581 | * no new races - i.e. only another task doing capget() on | ||
582 | * this task could get inconsistent info. There can be no | ||
583 | * racing writer bc a task can only change its own caps. | ||
584 | */ | ||
585 | long cap_prctl_drop(unsigned long cap) | ||
586 | { | ||
587 | if (!capable(CAP_SETPCAP)) | ||
588 | return -EPERM; | ||
589 | if (!cap_valid(cap)) | ||
590 | return -EINVAL; | ||
591 | cap_lower(current->cap_bset, cap); | ||
592 | return 0; | ||
593 | } | ||
564 | #else | 594 | #else |
565 | int cap_task_setscheduler (struct task_struct *p, int policy, | 595 | int cap_task_setscheduler (struct task_struct *p, int policy, |
566 | struct sched_param *lp) | 596 | struct sched_param *lp) |
@@ -584,9 +614,9 @@ int cap_task_kill(struct task_struct *p, struct siginfo *info, | |||
584 | 614 | ||
585 | void cap_task_reparent_to_init (struct task_struct *p) | 615 | void cap_task_reparent_to_init (struct task_struct *p) |
586 | { | 616 | { |
587 | p->cap_effective = CAP_INIT_EFF_SET; | 617 | cap_set_init_eff(p->cap_effective); |
588 | p->cap_inheritable = CAP_INIT_INH_SET; | 618 | cap_clear(p->cap_inheritable); |
589 | p->cap_permitted = CAP_FULL_SET; | 619 | cap_set_full(p->cap_permitted); |
590 | p->keep_capabilities = 0; | 620 | p->keep_capabilities = 0; |
591 | return; | 621 | return; |
592 | } | 622 | } |