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 /kernel/capability.c | |
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 'kernel/capability.c')
-rw-r--r-- | kernel/capability.c | 113 |
1 files changed, 104 insertions, 9 deletions
diff --git a/kernel/capability.c b/kernel/capability.c index efbd9cdce132..39e8193b41ea 100644 --- a/kernel/capability.c +++ b/kernel/capability.c | |||
@@ -22,6 +22,37 @@ | |||
22 | static DEFINE_SPINLOCK(task_capability_lock); | 22 | static DEFINE_SPINLOCK(task_capability_lock); |
23 | 23 | ||
24 | /* | 24 | /* |
25 | * Leveraged for setting/resetting capabilities | ||
26 | */ | ||
27 | |||
28 | const kernel_cap_t __cap_empty_set = CAP_EMPTY_SET; | ||
29 | const kernel_cap_t __cap_full_set = CAP_FULL_SET; | ||
30 | const kernel_cap_t __cap_init_eff_set = CAP_INIT_EFF_SET; | ||
31 | |||
32 | EXPORT_SYMBOL(__cap_empty_set); | ||
33 | EXPORT_SYMBOL(__cap_full_set); | ||
34 | EXPORT_SYMBOL(__cap_init_eff_set); | ||
35 | |||
36 | /* | ||
37 | * More recent versions of libcap are available from: | ||
38 | * | ||
39 | * http://www.kernel.org/pub/linux/libs/security/linux-privs/ | ||
40 | */ | ||
41 | |||
42 | static void warn_legacy_capability_use(void) | ||
43 | { | ||
44 | static int warned; | ||
45 | if (!warned) { | ||
46 | char name[sizeof(current->comm)]; | ||
47 | |||
48 | printk(KERN_INFO "warning: `%s' uses 32-bit capabilities" | ||
49 | " (legacy support in use)\n", | ||
50 | get_task_comm(name, current)); | ||
51 | warned = 1; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | /* | ||
25 | * For sys_getproccap() and sys_setproccap(), any of the three | 56 | * For sys_getproccap() and sys_setproccap(), any of the three |
26 | * capability set pointers may be NULL -- indicating that that set is | 57 | * capability set pointers may be NULL -- indicating that that set is |
27 | * uninteresting and/or not to be changed. | 58 | * uninteresting and/or not to be changed. |
@@ -42,12 +73,21 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) | |||
42 | pid_t pid; | 73 | pid_t pid; |
43 | __u32 version; | 74 | __u32 version; |
44 | struct task_struct *target; | 75 | struct task_struct *target; |
45 | struct __user_cap_data_struct data; | 76 | unsigned tocopy; |
77 | kernel_cap_t pE, pI, pP; | ||
46 | 78 | ||
47 | if (get_user(version, &header->version)) | 79 | if (get_user(version, &header->version)) |
48 | return -EFAULT; | 80 | return -EFAULT; |
49 | 81 | ||
50 | if (version != _LINUX_CAPABILITY_VERSION) { | 82 | switch (version) { |
83 | case _LINUX_CAPABILITY_VERSION_1: | ||
84 | warn_legacy_capability_use(); | ||
85 | tocopy = _LINUX_CAPABILITY_U32S_1; | ||
86 | break; | ||
87 | case _LINUX_CAPABILITY_VERSION_2: | ||
88 | tocopy = _LINUX_CAPABILITY_U32S_2; | ||
89 | break; | ||
90 | default: | ||
51 | if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) | 91 | if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) |
52 | return -EFAULT; | 92 | return -EFAULT; |
53 | return -EINVAL; | 93 | return -EINVAL; |
@@ -71,14 +111,47 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) | |||
71 | } else | 111 | } else |
72 | target = current; | 112 | target = current; |
73 | 113 | ||
74 | ret = security_capget(target, &data.effective, &data.inheritable, &data.permitted); | 114 | ret = security_capget(target, &pE, &pI, &pP); |
75 | 115 | ||
76 | out: | 116 | out: |
77 | read_unlock(&tasklist_lock); | 117 | read_unlock(&tasklist_lock); |
78 | spin_unlock(&task_capability_lock); | 118 | spin_unlock(&task_capability_lock); |
79 | 119 | ||
80 | if (!ret && copy_to_user(dataptr, &data, sizeof data)) | 120 | if (!ret) { |
81 | return -EFAULT; | 121 | struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S]; |
122 | unsigned i; | ||
123 | |||
124 | for (i = 0; i < tocopy; i++) { | ||
125 | kdata[i].effective = pE.cap[i]; | ||
126 | kdata[i].permitted = pP.cap[i]; | ||
127 | kdata[i].inheritable = pI.cap[i]; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Note, in the case, tocopy < _LINUX_CAPABILITY_U32S, | ||
132 | * we silently drop the upper capabilities here. This | ||
133 | * has the effect of making older libcap | ||
134 | * implementations implicitly drop upper capability | ||
135 | * bits when they perform a: capget/modify/capset | ||
136 | * sequence. | ||
137 | * | ||
138 | * This behavior is considered fail-safe | ||
139 | * behavior. Upgrading the application to a newer | ||
140 | * version of libcap will enable access to the newer | ||
141 | * capabilities. | ||
142 | * | ||
143 | * An alternative would be to return an error here | ||
144 | * (-ERANGE), but that causes legacy applications to | ||
145 | * unexpectidly fail; the capget/modify/capset aborts | ||
146 | * before modification is attempted and the application | ||
147 | * fails. | ||
148 | */ | ||
149 | |||
150 | if (copy_to_user(dataptr, kdata, tocopy | ||
151 | * sizeof(struct __user_cap_data_struct))) { | ||
152 | return -EFAULT; | ||
153 | } | ||
154 | } | ||
82 | 155 | ||
83 | return ret; | 156 | return ret; |
84 | } | 157 | } |
@@ -167,6 +240,8 @@ static inline int cap_set_all(kernel_cap_t *effective, | |||
167 | */ | 240 | */ |
168 | asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | 241 | asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) |
169 | { | 242 | { |
243 | struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S]; | ||
244 | unsigned i, tocopy; | ||
170 | kernel_cap_t inheritable, permitted, effective; | 245 | kernel_cap_t inheritable, permitted, effective; |
171 | __u32 version; | 246 | __u32 version; |
172 | struct task_struct *target; | 247 | struct task_struct *target; |
@@ -176,7 +251,15 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
176 | if (get_user(version, &header->version)) | 251 | if (get_user(version, &header->version)) |
177 | return -EFAULT; | 252 | return -EFAULT; |
178 | 253 | ||
179 | if (version != _LINUX_CAPABILITY_VERSION) { | 254 | switch (version) { |
255 | case _LINUX_CAPABILITY_VERSION_1: | ||
256 | warn_legacy_capability_use(); | ||
257 | tocopy = _LINUX_CAPABILITY_U32S_1; | ||
258 | break; | ||
259 | case _LINUX_CAPABILITY_VERSION_2: | ||
260 | tocopy = _LINUX_CAPABILITY_U32S_2; | ||
261 | break; | ||
262 | default: | ||
180 | if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) | 263 | if (put_user(_LINUX_CAPABILITY_VERSION, &header->version)) |
181 | return -EFAULT; | 264 | return -EFAULT; |
182 | return -EINVAL; | 265 | return -EINVAL; |
@@ -188,10 +271,22 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
188 | if (pid && pid != task_pid_vnr(current) && !capable(CAP_SETPCAP)) | 271 | if (pid && pid != task_pid_vnr(current) && !capable(CAP_SETPCAP)) |
189 | return -EPERM; | 272 | return -EPERM; |
190 | 273 | ||
191 | if (copy_from_user(&effective, &data->effective, sizeof(effective)) || | 274 | if (copy_from_user(&kdata, data, tocopy |
192 | copy_from_user(&inheritable, &data->inheritable, sizeof(inheritable)) || | 275 | * sizeof(struct __user_cap_data_struct))) { |
193 | copy_from_user(&permitted, &data->permitted, sizeof(permitted))) | ||
194 | return -EFAULT; | 276 | return -EFAULT; |
277 | } | ||
278 | |||
279 | for (i = 0; i < tocopy; i++) { | ||
280 | effective.cap[i] = kdata[i].effective; | ||
281 | permitted.cap[i] = kdata[i].permitted; | ||
282 | inheritable.cap[i] = kdata[i].inheritable; | ||
283 | } | ||
284 | while (i < _LINUX_CAPABILITY_U32S) { | ||
285 | effective.cap[i] = 0; | ||
286 | permitted.cap[i] = 0; | ||
287 | inheritable.cap[i] = 0; | ||
288 | i++; | ||
289 | } | ||
195 | 290 | ||
196 | spin_lock(&task_capability_lock); | 291 | spin_lock(&task_capability_lock); |
197 | read_lock(&tasklist_lock); | 292 | read_lock(&tasklist_lock); |