diff options
author | David Howells <dhowells@redhat.com> | 2008-11-13 18:39:14 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-11-13 18:39:14 -0500 |
commit | 1cdcbec1a3372c0c49c59d292e708fd07b509f18 (patch) | |
tree | d1bd302c8d66862da45b494cbc766fb4caa5e23e /kernel | |
parent | 8bbf4976b59fc9fc2861e79cab7beb3f6d647640 (diff) |
CRED: Neuter sys_capset()
Take away the ability for sys_capset() to affect processes other than current.
This means that current will not need to lock its own credentials when reading
them against interference by other processes.
This has effectively been the case for a while anyway, since:
(1) Without LSM enabled, sys_capset() is disallowed.
(2) With file-based capabilities, sys_capset() is neutered.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Acked-by: Andrew G. Morgan <morgan@kernel.org>
Acked-by: James Morris <jmorris@namei.org>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/capability.c | 227 |
1 files changed, 23 insertions, 204 deletions
diff --git a/kernel/capability.c b/kernel/capability.c index adb262f83de1..58b00519624a 100644 --- a/kernel/capability.c +++ b/kernel/capability.c | |||
@@ -127,160 +127,6 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy) | |||
127 | return 0; | 127 | return 0; |
128 | } | 128 | } |
129 | 129 | ||
130 | #ifndef CONFIG_SECURITY_FILE_CAPABILITIES | ||
131 | |||
132 | /* | ||
133 | * Without filesystem capability support, we nominally support one process | ||
134 | * setting the capabilities of another | ||
135 | */ | ||
136 | static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, | ||
137 | kernel_cap_t *pIp, kernel_cap_t *pPp) | ||
138 | { | ||
139 | struct task_struct *target; | ||
140 | int ret; | ||
141 | |||
142 | spin_lock(&task_capability_lock); | ||
143 | read_lock(&tasklist_lock); | ||
144 | |||
145 | if (pid && pid != task_pid_vnr(current)) { | ||
146 | target = find_task_by_vpid(pid); | ||
147 | if (!target) { | ||
148 | ret = -ESRCH; | ||
149 | goto out; | ||
150 | } | ||
151 | } else | ||
152 | target = current; | ||
153 | |||
154 | ret = security_capget(target, pEp, pIp, pPp); | ||
155 | |||
156 | out: | ||
157 | read_unlock(&tasklist_lock); | ||
158 | spin_unlock(&task_capability_lock); | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * cap_set_pg - set capabilities for all processes in a given process | ||
165 | * group. We call this holding task_capability_lock and tasklist_lock. | ||
166 | */ | ||
167 | static inline int cap_set_pg(int pgrp_nr, kernel_cap_t *effective, | ||
168 | kernel_cap_t *inheritable, | ||
169 | kernel_cap_t *permitted) | ||
170 | { | ||
171 | struct task_struct *g, *target; | ||
172 | int ret = -EPERM; | ||
173 | int found = 0; | ||
174 | struct pid *pgrp; | ||
175 | |||
176 | spin_lock(&task_capability_lock); | ||
177 | read_lock(&tasklist_lock); | ||
178 | |||
179 | pgrp = find_vpid(pgrp_nr); | ||
180 | do_each_pid_task(pgrp, PIDTYPE_PGID, g) { | ||
181 | target = g; | ||
182 | while_each_thread(g, target) { | ||
183 | if (!security_capset_check(target, effective, | ||
184 | inheritable, permitted)) { | ||
185 | security_capset_set(target, effective, | ||
186 | inheritable, permitted); | ||
187 | ret = 0; | ||
188 | } | ||
189 | found = 1; | ||
190 | } | ||
191 | } while_each_pid_task(pgrp, PIDTYPE_PGID, g); | ||
192 | |||
193 | read_unlock(&tasklist_lock); | ||
194 | spin_unlock(&task_capability_lock); | ||
195 | |||
196 | if (!found) | ||
197 | ret = 0; | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * cap_set_all - set capabilities for all processes other than init | ||
203 | * and self. We call this holding task_capability_lock and tasklist_lock. | ||
204 | */ | ||
205 | static inline int cap_set_all(kernel_cap_t *effective, | ||
206 | kernel_cap_t *inheritable, | ||
207 | kernel_cap_t *permitted) | ||
208 | { | ||
209 | struct task_struct *g, *target; | ||
210 | int ret = -EPERM; | ||
211 | int found = 0; | ||
212 | |||
213 | spin_lock(&task_capability_lock); | ||
214 | read_lock(&tasklist_lock); | ||
215 | |||
216 | do_each_thread(g, target) { | ||
217 | if (target == current | ||
218 | || is_container_init(target->group_leader)) | ||
219 | continue; | ||
220 | found = 1; | ||
221 | if (security_capset_check(target, effective, inheritable, | ||
222 | permitted)) | ||
223 | continue; | ||
224 | ret = 0; | ||
225 | security_capset_set(target, effective, inheritable, permitted); | ||
226 | } while_each_thread(g, target); | ||
227 | |||
228 | read_unlock(&tasklist_lock); | ||
229 | spin_unlock(&task_capability_lock); | ||
230 | |||
231 | if (!found) | ||
232 | ret = 0; | ||
233 | |||
234 | return ret; | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Given the target pid does not refer to the current process we | ||
239 | * need more elaborate support... (This support is not present when | ||
240 | * filesystem capabilities are configured.) | ||
241 | */ | ||
242 | static inline int do_sys_capset_other_tasks(pid_t pid, kernel_cap_t *effective, | ||
243 | kernel_cap_t *inheritable, | ||
244 | kernel_cap_t *permitted) | ||
245 | { | ||
246 | struct task_struct *target; | ||
247 | int ret; | ||
248 | |||
249 | if (!capable(CAP_SETPCAP)) | ||
250 | return -EPERM; | ||
251 | |||
252 | if (pid == -1) /* all procs other than current and init */ | ||
253 | return cap_set_all(effective, inheritable, permitted); | ||
254 | |||
255 | else if (pid < 0) /* all procs in process group */ | ||
256 | return cap_set_pg(-pid, effective, inheritable, permitted); | ||
257 | |||
258 | /* target != current */ | ||
259 | spin_lock(&task_capability_lock); | ||
260 | read_lock(&tasklist_lock); | ||
261 | |||
262 | target = find_task_by_vpid(pid); | ||
263 | if (!target) | ||
264 | ret = -ESRCH; | ||
265 | else { | ||
266 | ret = security_capset_check(target, effective, inheritable, | ||
267 | permitted); | ||
268 | |||
269 | /* having verified that the proposed changes are legal, | ||
270 | we now put them into effect. */ | ||
271 | if (!ret) | ||
272 | security_capset_set(target, effective, inheritable, | ||
273 | permitted); | ||
274 | } | ||
275 | |||
276 | read_unlock(&tasklist_lock); | ||
277 | spin_unlock(&task_capability_lock); | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | #else /* ie., def CONFIG_SECURITY_FILE_CAPABILITIES */ | ||
283 | |||
284 | /* | 130 | /* |
285 | * If we have configured with filesystem capability support, then the | 131 | * If we have configured with filesystem capability support, then the |
286 | * only thing that can change the capabilities of the current process | 132 | * only thing that can change the capabilities of the current process |
@@ -315,22 +161,6 @@ static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp, | |||
315 | } | 161 | } |
316 | 162 | ||
317 | /* | 163 | /* |
318 | * With filesystem capability support configured, the kernel does not | ||
319 | * permit the changing of capabilities in one process by another | ||
320 | * process. (CAP_SETPCAP has much less broad semantics when configured | ||
321 | * this way.) | ||
322 | */ | ||
323 | static inline int do_sys_capset_other_tasks(pid_t pid, | ||
324 | kernel_cap_t *effective, | ||
325 | kernel_cap_t *inheritable, | ||
326 | kernel_cap_t *permitted) | ||
327 | { | ||
328 | return -EPERM; | ||
329 | } | ||
330 | |||
331 | #endif /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */ | ||
332 | |||
333 | /* | ||
334 | * Atomically modify the effective capabilities returning the original | 164 | * Atomically modify the effective capabilities returning the original |
335 | * value. No permission check is performed here - it is assumed that the | 165 | * value. No permission check is performed here - it is assumed that the |
336 | * caller is permitted to set the desired effective capabilities. | 166 | * caller is permitted to set the desired effective capabilities. |
@@ -424,16 +254,14 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr) | |||
424 | * @data: pointer to struct that contains the effective, permitted, | 254 | * @data: pointer to struct that contains the effective, permitted, |
425 | * and inheritable capabilities | 255 | * and inheritable capabilities |
426 | * | 256 | * |
427 | * Set capabilities for a given process, all processes, or all | 257 | * Set capabilities for the current process only. The ability to any other |
428 | * processes in a given process group. | 258 | * process(es) has been deprecated and removed. |
429 | * | 259 | * |
430 | * The restrictions on setting capabilities are specified as: | 260 | * The restrictions on setting capabilities are specified as: |
431 | * | 261 | * |
432 | * [pid is for the 'target' task. 'current' is the calling task.] | 262 | * I: any raised capabilities must be a subset of the old permitted |
433 | * | 263 | * P: any raised capabilities must be a subset of the old permitted |
434 | * I: any raised capabilities must be a subset of the (old current) permitted | 264 | * E: must be set to a subset of new permitted |
435 | * P: any raised capabilities must be a subset of the (old current) permitted | ||
436 | * E: must be set to a subset of (new target) permitted | ||
437 | * | 265 | * |
438 | * Returns 0 on success and < 0 on error. | 266 | * Returns 0 on success and < 0 on error. |
439 | */ | 267 | */ |
@@ -452,10 +280,13 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
452 | if (get_user(pid, &header->pid)) | 280 | if (get_user(pid, &header->pid)) |
453 | return -EFAULT; | 281 | return -EFAULT; |
454 | 282 | ||
283 | /* may only affect current now */ | ||
284 | if (pid != 0 && pid != task_pid_vnr(current)) | ||
285 | return -EPERM; | ||
286 | |||
455 | if (copy_from_user(&kdata, data, tocopy | 287 | if (copy_from_user(&kdata, data, tocopy |
456 | * sizeof(struct __user_cap_data_struct))) { | 288 | * sizeof(struct __user_cap_data_struct))) |
457 | return -EFAULT; | 289 | return -EFAULT; |
458 | } | ||
459 | 290 | ||
460 | for (i = 0; i < tocopy; i++) { | 291 | for (i = 0; i < tocopy; i++) { |
461 | effective.cap[i] = kdata[i].effective; | 292 | effective.cap[i] = kdata[i].effective; |
@@ -473,32 +304,20 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data) | |||
473 | if (ret) | 304 | if (ret) |
474 | return ret; | 305 | return ret; |
475 | 306 | ||
476 | if (pid && (pid != task_pid_vnr(current))) | 307 | /* This lock is required even when filesystem capability support is |
477 | ret = do_sys_capset_other_tasks(pid, &effective, &inheritable, | 308 | * configured - it protects the sys_capget() call from returning |
478 | &permitted); | 309 | * incorrect data in the case that the targeted process is not the |
479 | else { | 310 | * current one. |
480 | /* | 311 | */ |
481 | * This lock is required even when filesystem | 312 | spin_lock(&task_capability_lock); |
482 | * capability support is configured - it protects the | ||
483 | * sys_capget() call from returning incorrect data in | ||
484 | * the case that the targeted process is not the | ||
485 | * current one. | ||
486 | */ | ||
487 | spin_lock(&task_capability_lock); | ||
488 | |||
489 | ret = security_capset_check(current, &effective, &inheritable, | ||
490 | &permitted); | ||
491 | /* | ||
492 | * Having verified that the proposed changes are | ||
493 | * legal, we now put them into effect. | ||
494 | */ | ||
495 | if (!ret) | ||
496 | security_capset_set(current, &effective, &inheritable, | ||
497 | &permitted); | ||
498 | spin_unlock(&task_capability_lock); | ||
499 | } | ||
500 | |||
501 | 313 | ||
314 | ret = security_capset_check(&effective, &inheritable, &permitted); | ||
315 | /* Having verified that the proposed changes are legal, we now put them | ||
316 | * into effect. | ||
317 | */ | ||
318 | if (!ret) | ||
319 | security_capset_set(&effective, &inheritable, &permitted); | ||
320 | spin_unlock(&task_capability_lock); | ||
502 | return ret; | 321 | return ret; |
503 | } | 322 | } |
504 | 323 | ||