diff options
Diffstat (limited to 'security/commoncap.c')
-rw-r--r-- | security/commoncap.c | 152 |
1 files changed, 76 insertions, 76 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index b5419273f92d..51dfa11e8e56 100644 --- a/security/commoncap.c +++ b/security/commoncap.c | |||
@@ -167,7 +167,7 @@ int cap_capset(struct cred *new, | |||
167 | 167 | ||
168 | static inline void bprm_clear_caps(struct linux_binprm *bprm) | 168 | static inline void bprm_clear_caps(struct linux_binprm *bprm) |
169 | { | 169 | { |
170 | cap_clear(bprm->cap_post_exec_permitted); | 170 | cap_clear(bprm->cred->cap_permitted); |
171 | bprm->cap_effective = false; | 171 | bprm->cap_effective = false; |
172 | } | 172 | } |
173 | 173 | ||
@@ -198,15 +198,15 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
198 | } | 198 | } |
199 | 199 | ||
200 | static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, | 200 | static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, |
201 | struct linux_binprm *bprm) | 201 | struct linux_binprm *bprm, |
202 | bool *effective) | ||
202 | { | 203 | { |
204 | struct cred *new = bprm->cred; | ||
203 | unsigned i; | 205 | unsigned i; |
204 | int ret = 0; | 206 | int ret = 0; |
205 | 207 | ||
206 | if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) | 208 | if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) |
207 | bprm->cap_effective = true; | 209 | *effective = true; |
208 | else | ||
209 | bprm->cap_effective = false; | ||
210 | 210 | ||
211 | CAP_FOR_EACH_U32(i) { | 211 | CAP_FOR_EACH_U32(i) { |
212 | __u32 permitted = caps->permitted.cap[i]; | 212 | __u32 permitted = caps->permitted.cap[i]; |
@@ -215,16 +215,13 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, | |||
215 | /* | 215 | /* |
216 | * pP' = (X & fP) | (pI & fI) | 216 | * pP' = (X & fP) | (pI & fI) |
217 | */ | 217 | */ |
218 | bprm->cap_post_exec_permitted.cap[i] = | 218 | new->cap_permitted.cap[i] = |
219 | (current->cred->cap_bset.cap[i] & permitted) | | 219 | (new->cap_bset.cap[i] & permitted) | |
220 | (current->cred->cap_inheritable.cap[i] & inheritable); | 220 | (new->cap_inheritable.cap[i] & inheritable); |
221 | 221 | ||
222 | if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) { | 222 | if (permitted & ~new->cap_permitted.cap[i]) |
223 | /* | 223 | /* insufficient to execute correctly */ |
224 | * insufficient to execute correctly | ||
225 | */ | ||
226 | ret = -EPERM; | 224 | ret = -EPERM; |
227 | } | ||
228 | } | 225 | } |
229 | 226 | ||
230 | /* | 227 | /* |
@@ -232,7 +229,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, | |||
232 | * do not have enough capabilities, we return an error if they are | 229 | * do not have enough capabilities, we return an error if they are |
233 | * missing some "forced" (aka file-permitted) capabilities. | 230 | * missing some "forced" (aka file-permitted) capabilities. |
234 | */ | 231 | */ |
235 | return bprm->cap_effective ? ret : 0; | 232 | return *effective ? ret : 0; |
236 | } | 233 | } |
237 | 234 | ||
238 | int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) | 235 | int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data *cpu_caps) |
@@ -250,10 +247,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data | |||
250 | 247 | ||
251 | size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps, | 248 | size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps, |
252 | XATTR_CAPS_SZ); | 249 | XATTR_CAPS_SZ); |
253 | if (size == -ENODATA || size == -EOPNOTSUPP) { | 250 | if (size == -ENODATA || size == -EOPNOTSUPP) |
254 | /* no data, that's ok */ | 251 | /* no data, that's ok */ |
255 | return -ENODATA; | 252 | return -ENODATA; |
256 | } | ||
257 | if (size < 0) | 253 | if (size < 0) |
258 | return size; | 254 | return size; |
259 | 255 | ||
@@ -262,7 +258,7 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data | |||
262 | 258 | ||
263 | cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc); | 259 | cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc); |
264 | 260 | ||
265 | switch ((magic_etc & VFS_CAP_REVISION_MASK)) { | 261 | switch (magic_etc & VFS_CAP_REVISION_MASK) { |
266 | case VFS_CAP_REVISION_1: | 262 | case VFS_CAP_REVISION_1: |
267 | if (size != XATTR_CAPS_SZ_1) | 263 | if (size != XATTR_CAPS_SZ_1) |
268 | return -EINVAL; | 264 | return -EINVAL; |
@@ -283,11 +279,12 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data | |||
283 | cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted); | 279 | cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted); |
284 | cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); | 280 | cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); |
285 | } | 281 | } |
282 | |||
286 | return 0; | 283 | return 0; |
287 | } | 284 | } |
288 | 285 | ||
289 | /* Locate any VFS capabilities: */ | 286 | /* Locate any VFS capabilities: */ |
290 | static int get_file_caps(struct linux_binprm *bprm) | 287 | static int get_file_caps(struct linux_binprm *bprm, bool *effective) |
291 | { | 288 | { |
292 | struct dentry *dentry; | 289 | struct dentry *dentry; |
293 | int rc = 0; | 290 | int rc = 0; |
@@ -313,7 +310,10 @@ static int get_file_caps(struct linux_binprm *bprm) | |||
313 | goto out; | 310 | goto out; |
314 | } | 311 | } |
315 | 312 | ||
316 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm); | 313 | rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective); |
314 | if (rc == -EINVAL) | ||
315 | printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n", | ||
316 | __func__, rc, bprm->filename); | ||
317 | 317 | ||
318 | out: | 318 | out: |
319 | dput(dentry); | 319 | dput(dentry); |
@@ -334,18 +334,27 @@ int cap_inode_killpriv(struct dentry *dentry) | |||
334 | return 0; | 334 | return 0; |
335 | } | 335 | } |
336 | 336 | ||
337 | static inline int get_file_caps(struct linux_binprm *bprm) | 337 | static inline int get_file_caps(struct linux_binprm *bprm, bool *effective) |
338 | { | 338 | { |
339 | bprm_clear_caps(bprm); | 339 | bprm_clear_caps(bprm); |
340 | return 0; | 340 | return 0; |
341 | } | 341 | } |
342 | #endif | 342 | #endif |
343 | 343 | ||
344 | int cap_bprm_set_security (struct linux_binprm *bprm) | 344 | /* |
345 | * set up the new credentials for an exec'd task | ||
346 | */ | ||
347 | int cap_bprm_set_creds(struct linux_binprm *bprm) | ||
345 | { | 348 | { |
349 | const struct cred *old = current_cred(); | ||
350 | struct cred *new = bprm->cred; | ||
351 | bool effective; | ||
346 | int ret; | 352 | int ret; |
347 | 353 | ||
348 | ret = get_file_caps(bprm); | 354 | effective = false; |
355 | ret = get_file_caps(bprm, &effective); | ||
356 | if (ret < 0) | ||
357 | return ret; | ||
349 | 358 | ||
350 | if (!issecure(SECURE_NOROOT)) { | 359 | if (!issecure(SECURE_NOROOT)) { |
351 | /* | 360 | /* |
@@ -353,63 +362,47 @@ int cap_bprm_set_security (struct linux_binprm *bprm) | |||
353 | * executables under compatibility mode, we override the | 362 | * executables under compatibility mode, we override the |
354 | * capability sets for the file. | 363 | * capability sets for the file. |
355 | * | 364 | * |
356 | * If only the real uid is 0, we do not set the effective | 365 | * If only the real uid is 0, we do not set the effective bit. |
357 | * bit. | ||
358 | */ | 366 | */ |
359 | if (bprm->e_uid == 0 || current_uid() == 0) { | 367 | if (new->euid == 0 || new->uid == 0) { |
360 | /* pP' = (cap_bset & ~0) | (pI & ~0) */ | 368 | /* pP' = (cap_bset & ~0) | (pI & ~0) */ |
361 | bprm->cap_post_exec_permitted = cap_combine( | 369 | new->cap_permitted = cap_combine(old->cap_bset, |
362 | current->cred->cap_bset, | 370 | old->cap_inheritable); |
363 | current->cred->cap_inheritable); | ||
364 | bprm->cap_effective = (bprm->e_uid == 0); | ||
365 | ret = 0; | ||
366 | } | 371 | } |
372 | if (new->euid == 0) | ||
373 | effective = true; | ||
367 | } | 374 | } |
368 | 375 | ||
369 | return ret; | 376 | /* Don't let someone trace a set[ug]id/setpcap binary with the revised |
370 | } | 377 | * credentials unless they have the appropriate permit |
371 | 378 | */ | |
372 | int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 379 | if ((new->euid != old->uid || |
373 | { | 380 | new->egid != old->gid || |
374 | const struct cred *old = current_cred(); | 381 | !cap_issubset(new->cap_permitted, old->cap_permitted)) && |
375 | struct cred *new; | 382 | bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { |
376 | 383 | /* downgrade; they get no more than they had, and maybe less */ | |
377 | new = prepare_creds(); | 384 | if (!capable(CAP_SETUID)) { |
378 | if (!new) | 385 | new->euid = new->uid; |
379 | return -ENOMEM; | 386 | new->egid = new->gid; |
380 | |||
381 | if (bprm->e_uid != old->uid || bprm->e_gid != old->gid || | ||
382 | !cap_issubset(bprm->cap_post_exec_permitted, | ||
383 | old->cap_permitted)) { | ||
384 | set_dumpable(current->mm, suid_dumpable); | ||
385 | current->pdeath_signal = 0; | ||
386 | |||
387 | if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { | ||
388 | if (!capable(CAP_SETUID)) { | ||
389 | bprm->e_uid = old->uid; | ||
390 | bprm->e_gid = old->gid; | ||
391 | } | ||
392 | if (cap_limit_ptraced_target()) { | ||
393 | bprm->cap_post_exec_permitted = cap_intersect( | ||
394 | bprm->cap_post_exec_permitted, | ||
395 | new->cap_permitted); | ||
396 | } | ||
397 | } | 387 | } |
388 | if (cap_limit_ptraced_target()) | ||
389 | new->cap_permitted = cap_intersect(new->cap_permitted, | ||
390 | old->cap_permitted); | ||
398 | } | 391 | } |
399 | 392 | ||
400 | new->suid = new->euid = new->fsuid = bprm->e_uid; | 393 | new->suid = new->fsuid = new->euid; |
401 | new->sgid = new->egid = new->fsgid = bprm->e_gid; | 394 | new->sgid = new->fsgid = new->egid; |
402 | 395 | ||
403 | /* For init, we want to retain the capabilities set | 396 | /* For init, we want to retain the capabilities set in the initial |
404 | * in the init_task struct. Thus we skip the usual | 397 | * task. Thus we skip the usual capability rules |
405 | * capability rules */ | 398 | */ |
406 | if (!is_global_init(current)) { | 399 | if (!is_global_init(current)) { |
407 | new->cap_permitted = bprm->cap_post_exec_permitted; | 400 | if (effective) |
408 | if (bprm->cap_effective) | 401 | new->cap_effective = new->cap_permitted; |
409 | new->cap_effective = bprm->cap_post_exec_permitted; | ||
410 | else | 402 | else |
411 | cap_clear(new->cap_effective); | 403 | cap_clear(new->cap_effective); |
412 | } | 404 | } |
405 | bprm->cap_effective = effective; | ||
413 | 406 | ||
414 | /* | 407 | /* |
415 | * Audit candidate if current->cap_effective is set | 408 | * Audit candidate if current->cap_effective is set |
@@ -425,23 +418,31 @@ int cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | |||
425 | */ | 418 | */ |
426 | if (!cap_isclear(new->cap_effective)) { | 419 | if (!cap_isclear(new->cap_effective)) { |
427 | if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || | 420 | if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || |
428 | bprm->e_uid != 0 || new->uid != 0 || | 421 | new->euid != 0 || new->uid != 0 || |
429 | issecure(SECURE_NOROOT)) | 422 | issecure(SECURE_NOROOT)) { |
430 | audit_log_bprm_fcaps(bprm, new, old); | 423 | ret = audit_log_bprm_fcaps(bprm, new, old); |
424 | if (ret < 0) | ||
425 | return ret; | ||
426 | } | ||
431 | } | 427 | } |
432 | 428 | ||
433 | new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); | 429 | new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); |
434 | return commit_creds(new); | 430 | return 0; |
435 | } | 431 | } |
436 | 432 | ||
437 | int cap_bprm_secureexec (struct linux_binprm *bprm) | 433 | /* |
434 | * determine whether a secure execution is required | ||
435 | * - the creds have been committed at this point, and are no longer available | ||
436 | * through bprm | ||
437 | */ | ||
438 | int cap_bprm_secureexec(struct linux_binprm *bprm) | ||
438 | { | 439 | { |
439 | const struct cred *cred = current_cred(); | 440 | const struct cred *cred = current_cred(); |
440 | 441 | ||
441 | if (cred->uid != 0) { | 442 | if (cred->uid != 0) { |
442 | if (bprm->cap_effective) | 443 | if (bprm->cap_effective) |
443 | return 1; | 444 | return 1; |
444 | if (!cap_isclear(bprm->cap_post_exec_permitted)) | 445 | if (!cap_isclear(cred->cap_permitted)) |
445 | return 1; | 446 | return 1; |
446 | } | 447 | } |
447 | 448 | ||
@@ -477,7 +478,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) | |||
477 | } | 478 | } |
478 | 479 | ||
479 | /* moved from kernel/sys.c. */ | 480 | /* moved from kernel/sys.c. */ |
480 | /* | 481 | /* |
481 | * cap_emulate_setxuid() fixes the effective / permitted capabilities of | 482 | * cap_emulate_setxuid() fixes the effective / permitted capabilities of |
482 | * a process after a call to setuid, setreuid, or setresuid. | 483 | * a process after a call to setuid, setreuid, or setresuid. |
483 | * | 484 | * |
@@ -491,10 +492,10 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) | |||
491 | * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective | 492 | * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective |
492 | * capabilities are set to the permitted capabilities. | 493 | * capabilities are set to the permitted capabilities. |
493 | * | 494 | * |
494 | * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should | 495 | * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should |
495 | * never happen. | 496 | * never happen. |
496 | * | 497 | * |
497 | * -astor | 498 | * -astor |
498 | * | 499 | * |
499 | * cevans - New behaviour, Oct '99 | 500 | * cevans - New behaviour, Oct '99 |
500 | * A process may, via prctl(), elect to keep its capabilities when it | 501 | * A process may, via prctl(), elect to keep its capabilities when it |
@@ -751,4 +752,3 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) | |||
751 | cap_sys_admin = 1; | 752 | cap_sys_admin = 1; |
752 | return __vm_enough_memory(mm, pages, cap_sys_admin); | 753 | return __vm_enough_memory(mm, pages, cap_sys_admin); |
753 | } | 754 | } |
754 | |||