diff options
author | Andrew Morgan <morgan@kernel.org> | 2008-02-05 01:29:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-05 12:44:20 -0500 |
commit | e338d263a76af78fe8f38a72131188b58fceb591 (patch) | |
tree | f3f046fc6fd66de43de7191830f0daf3bc4ec8eb /security | |
parent | 8f6936f4d29aa14e54a2470b954a2e1f96322988 (diff) |
Add 64-bit capability support to the kernel
The patch supports legacy (32-bit) capability userspace, and where possible
translates 32-bit capabilities to/from userspace and the VFS to 64-bit
kernel space capabilities. If a capability set cannot be compressed into
32-bits for consumption by user space, the system call fails, with -ERANGE.
FWIW libcap-2.00 supports this change (and earlier capability formats)
http://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/
[akpm@linux-foundation.org: coding-syle fixes]
[akpm@linux-foundation.org: use get_task_comm()]
[ezk@cs.sunysb.edu: build fix]
[akpm@linux-foundation.org: do not initialise statics to 0 or NULL]
[akpm@linux-foundation.org: unused var]
[serue@us.ibm.com: export __cap_ symbols]
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: James Morris <jmorris@namei.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'security')
-rw-r--r-- | security/commoncap.c | 87 | ||||
-rw-r--r-- | security/dummy.c | 17 |
2 files changed, 68 insertions, 36 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index b06617b35b93..01ab47845dcf 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 |
@@ -93,9 +93,9 @@ int cap_capget (struct task_struct *target, kernel_cap_t *effective, | |||
93 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | 93 | kernel_cap_t *inheritable, kernel_cap_t *permitted) |
94 | { | 94 | { |
95 | /* Derived from kernel/capability.c:sys_capget. */ | 95 | /* Derived from kernel/capability.c:sys_capget. */ |
96 | *effective = cap_t (target->cap_effective); | 96 | *effective = target->cap_effective; |
97 | *inheritable = cap_t (target->cap_inheritable); | 97 | *inheritable = target->cap_inheritable; |
98 | *permitted = cap_t (target->cap_permitted); | 98 | *permitted = target->cap_permitted; |
99 | return 0; | 99 | return 0; |
100 | } | 100 | } |
101 | 101 | ||
@@ -197,28 +197,51 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
197 | return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); | 197 | return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS); |
198 | } | 198 | } |
199 | 199 | ||
200 | static inline int cap_from_disk(__le32 *caps, struct linux_binprm *bprm, | 200 | static inline int cap_from_disk(struct vfs_cap_data *caps, |
201 | int size) | 201 | struct linux_binprm *bprm, unsigned size) |
202 | { | 202 | { |
203 | __u32 magic_etc; | 203 | __u32 magic_etc; |
204 | unsigned tocopy, i; | ||
204 | 205 | ||
205 | if (size != XATTR_CAPS_SZ) | 206 | if (size < sizeof(magic_etc)) |
206 | return -EINVAL; | 207 | return -EINVAL; |
207 | 208 | ||
208 | magic_etc = le32_to_cpu(caps[0]); | 209 | magic_etc = le32_to_cpu(caps->magic_etc); |
209 | 210 | ||
210 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { | 211 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { |
211 | case VFS_CAP_REVISION: | 212 | case VFS_CAP_REVISION_1: |
212 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | 213 | if (size != XATTR_CAPS_SZ_1) |
213 | bprm->cap_effective = true; | 214 | return -EINVAL; |
214 | else | 215 | tocopy = VFS_CAP_U32_1; |
215 | bprm->cap_effective = false; | 216 | break; |
216 | bprm->cap_permitted = to_cap_t(le32_to_cpu(caps[1])); | 217 | case VFS_CAP_REVISION_2: |
217 | bprm->cap_inheritable = to_cap_t(le32_to_cpu(caps[2])); | 218 | if (size != XATTR_CAPS_SZ_2) |
218 | return 0; | 219 | return -EINVAL; |
220 | tocopy = VFS_CAP_U32_2; | ||
221 | break; | ||
219 | default: | 222 | default: |
220 | return -EINVAL; | 223 | return -EINVAL; |
221 | } | 224 | } |
225 | |||
226 | if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) { | ||
227 | bprm->cap_effective = true; | ||
228 | } else { | ||
229 | bprm->cap_effective = false; | ||
230 | } | ||
231 | |||
232 | for (i = 0; i < tocopy; ++i) { | ||
233 | bprm->cap_permitted.cap[i] = | ||
234 | le32_to_cpu(caps->data[i].permitted); | ||
235 | bprm->cap_inheritable.cap[i] = | ||
236 | le32_to_cpu(caps->data[i].inheritable); | ||
237 | } | ||
238 | while (i < VFS_CAP_U32) { | ||
239 | bprm->cap_permitted.cap[i] = 0; | ||
240 | bprm->cap_inheritable.cap[i] = 0; | ||
241 | i++; | ||
242 | } | ||
243 | |||
244 | return 0; | ||
222 | } | 245 | } |
223 | 246 | ||
224 | /* Locate any VFS capabilities: */ | 247 | /* Locate any VFS capabilities: */ |
@@ -226,7 +249,7 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
226 | { | 249 | { |
227 | struct dentry *dentry; | 250 | struct dentry *dentry; |
228 | int rc = 0; | 251 | int rc = 0; |
229 | __le32 v1caps[XATTR_CAPS_SZ]; | 252 | struct vfs_cap_data vcaps; |
230 | struct inode *inode; | 253 | struct inode *inode; |
231 | 254 | ||
232 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { | 255 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) { |
@@ -239,8 +262,8 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
239 | if (!inode->i_op || !inode->i_op->getxattr) | 262 | if (!inode->i_op || !inode->i_op->getxattr) |
240 | goto out; | 263 | goto out; |
241 | 264 | ||
242 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &v1caps, | 265 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps, |
243 | XATTR_CAPS_SZ); | 266 | XATTR_CAPS_SZ); |
244 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { | 267 | if (rc == -ENODATA || rc == -EOPNOTSUPP) { |
245 | /* no data, that's ok */ | 268 | /* no data, that's ok */ |
246 | rc = 0; | 269 | rc = 0; |
@@ -249,7 +272,7 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
249 | if (rc < 0) | 272 | if (rc < 0) |
250 | goto out; | 273 | goto out; |
251 | 274 | ||
252 | rc = cap_from_disk(v1caps, bprm, rc); | 275 | rc = cap_from_disk(&vcaps, bprm, rc); |
253 | if (rc) | 276 | if (rc) |
254 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | 277 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", |
255 | __FUNCTION__, rc, bprm->filename); | 278 | __FUNCTION__, rc, bprm->filename); |
@@ -344,8 +367,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
344 | * capability rules */ | 367 | * capability rules */ |
345 | if (!is_global_init(current)) { | 368 | if (!is_global_init(current)) { |
346 | current->cap_permitted = new_permitted; | 369 | current->cap_permitted = new_permitted; |
347 | current->cap_effective = bprm->cap_effective ? | 370 | if (bprm->cap_effective) |
348 | new_permitted : 0; | 371 | current->cap_effective = new_permitted; |
372 | else | ||
373 | cap_clear(current->cap_effective); | ||
349 | } | 374 | } |
350 | 375 | ||
351 | /* AUD: Audit candidate if current->cap_effective is set */ | 376 | /* AUD: Audit candidate if current->cap_effective is set */ |
@@ -467,13 +492,15 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, | |||
467 | 492 | ||
468 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { | 493 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { |
469 | if (old_fsuid == 0 && current->fsuid != 0) { | 494 | if (old_fsuid == 0 && current->fsuid != 0) { |
470 | cap_t (current->cap_effective) &= | 495 | current->cap_effective = |
471 | ~CAP_FS_MASK; | 496 | cap_drop_fs_set( |
497 | current->cap_effective); | ||
472 | } | 498 | } |
473 | if (old_fsuid != 0 && current->fsuid == 0) { | 499 | if (old_fsuid != 0 && current->fsuid == 0) { |
474 | cap_t (current->cap_effective) |= | 500 | current->cap_effective = |
475 | (cap_t (current->cap_permitted) & | 501 | cap_raise_fs_set( |
476 | CAP_FS_MASK); | 502 | current->cap_effective, |
503 | current->cap_permitted); | ||
477 | } | 504 | } |
478 | } | 505 | } |
479 | break; | 506 | break; |
@@ -577,9 +604,9 @@ int cap_task_kill(struct task_struct *p, struct siginfo *info, | |||
577 | 604 | ||
578 | void cap_task_reparent_to_init (struct task_struct *p) | 605 | void cap_task_reparent_to_init (struct task_struct *p) |
579 | { | 606 | { |
580 | p->cap_effective = CAP_INIT_EFF_SET; | 607 | cap_set_init_eff(p->cap_effective); |
581 | p->cap_inheritable = CAP_INIT_INH_SET; | 608 | cap_clear(p->cap_inheritable); |
582 | p->cap_permitted = CAP_FULL_SET; | 609 | cap_set_full(p->cap_permitted); |
583 | p->keep_capabilities = 0; | 610 | p->keep_capabilities = 0; |
584 | return; | 611 | return; |
585 | } | 612 | } |
diff --git a/security/dummy.c b/security/dummy.c index c505122e22db..649326bf64ea 100644 --- a/security/dummy.c +++ b/security/dummy.c | |||
@@ -36,14 +36,19 @@ static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) | |||
36 | static int dummy_capget (struct task_struct *target, kernel_cap_t * effective, | 36 | static int dummy_capget (struct task_struct *target, kernel_cap_t * effective, |
37 | kernel_cap_t * inheritable, kernel_cap_t * permitted) | 37 | kernel_cap_t * inheritable, kernel_cap_t * permitted) |
38 | { | 38 | { |
39 | *effective = *inheritable = *permitted = 0; | ||
40 | if (target->euid == 0) { | 39 | if (target->euid == 0) { |
41 | *permitted |= (~0 & ~CAP_FS_MASK); | 40 | cap_set_full(*permitted); |
42 | *effective |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP) & ~CAP_FS_MASK); | 41 | cap_set_init_eff(*effective); |
42 | } else { | ||
43 | cap_clear(*permitted); | ||
44 | cap_clear(*effective); | ||
43 | } | 45 | } |
44 | if (target->fsuid == 0) { | 46 | |
45 | *permitted |= CAP_FS_MASK; | 47 | cap_clear(*inheritable); |
46 | *effective |= CAP_FS_MASK; | 48 | |
49 | if (target->fsuid != 0) { | ||
50 | *permitted = cap_drop_fs_set(*permitted); | ||
51 | *effective = cap_drop_fs_set(*effective); | ||
47 | } | 52 | } |
48 | return 0; | 53 | return 0; |
49 | } | 54 | } |