aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2008-04-28 05:13:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-28 11:58:26 -0400
commit3898b1b4ebff8dcfbcf1807e0661585e06c9a91c (patch)
tree69a338864dfe654f68064a599c5d0da460df34ac /security
parent4016a1390d07f15b267eecb20e76a48fd5c524ef (diff)
capabilities: implement per-process securebits
Filesystem capability support makes it possible to do away with (set)uid-0 based privilege and use capabilities instead. That is, with filesystem support for capabilities but without this present patch, it is (conceptually) possible to manage a system with capabilities alone and never need to obtain privilege via (set)uid-0. Of course, conceptually isn't quite the same as currently possible since few user applications, certainly not enough to run a viable system, are currently prepared to leverage capabilities to exercise privilege. Further, many applications exist that may never get upgraded in this way, and the kernel will continue to want to support their setuid-0 base privilege needs. Where pure-capability applications evolve and replace setuid-0 binaries, it is desirable that there be a mechanisms by which they can contain their privilege. In addition to leveraging the per-process bounding and inheritable sets, this should include suppressing the privilege of the uid-0 superuser from the process' tree of children. The feature added by this patch can be leveraged to suppress the privilege associated with (set)uid-0. This suppression requires CAP_SETPCAP to initiate, and only immediately affects the 'current' process (it is inherited through fork()/exec()). This reimplementation differs significantly from the historical support for securebits which was system-wide, unwieldy and which has ultimately withered to a dead relic in the source of the modern kernel. With this patch applied a process, that is capable(CAP_SETPCAP), can now drop all legacy privilege (through uid=0) for itself and all subsequently fork()'d/exec()'d children with: prctl(PR_SET_SECUREBITS, 0x2f); This patch represents a no-op unless CONFIG_SECURITY_FILE_CAPABILITIES is enabled at configure time. [akpm@linux-foundation.org: fix uninitialised var warning] [serue@us.ibm.com: capabilities: use cap_task_prctl when !CONFIG_SECURITY] Signed-off-by: Andrew G. Morgan <morgan@kernel.org> Acked-by: Serge Hallyn <serue@us.ibm.com> Reviewed-by: James Morris <jmorris@namei.org> Cc: Stephen Smalley <sds@tycho.nsa.gov> Cc: Paul Moore <paul.moore@hp.com> Signed-off-by: Serge E. Hallyn <serue@us.ibm.com> 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/capability.c1
-rw-r--r--security/commoncap.c103
-rw-r--r--security/dummy.c2
-rw-r--r--security/security.c4
-rw-r--r--security/selinux/hooks.c5
5 files changed, 101 insertions, 14 deletions
diff --git a/security/capability.c b/security/capability.c
index 2c6e06d18fab..38ac54e3aed1 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -44,6 +44,7 @@ static struct security_operations capability_ops = {
44 .task_setioprio = cap_task_setioprio, 44 .task_setioprio = cap_task_setioprio,
45 .task_setnice = cap_task_setnice, 45 .task_setnice = cap_task_setnice,
46 .task_post_setuid = cap_task_post_setuid, 46 .task_post_setuid = cap_task_post_setuid,
47 .task_prctl = cap_task_prctl,
47 .task_reparent_to_init = cap_task_reparent_to_init, 48 .task_reparent_to_init = cap_task_reparent_to_init,
48 49
49 .syslog = cap_syslog, 50 .syslog = cap_syslog,
diff --git a/security/commoncap.c b/security/commoncap.c
index 852905789caf..e8c3f5e46705 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -24,11 +24,8 @@
24#include <linux/hugetlb.h> 24#include <linux/hugetlb.h>
25#include <linux/mount.h> 25#include <linux/mount.h>
26#include <linux/sched.h> 26#include <linux/sched.h>
27 27#include <linux/prctl.h>
28/* Global security state */ 28#include <linux/securebits.h>
29
30unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
31EXPORT_SYMBOL(securebits);
32 29
33int cap_netlink_send(struct sock *sk, struct sk_buff *skb) 30int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
34{ 31{
@@ -368,7 +365,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
368 365
369 /* AUD: Audit candidate if current->cap_effective is set */ 366 /* AUD: Audit candidate if current->cap_effective is set */
370 367
371 current->keep_capabilities = 0; 368 current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
372} 369}
373 370
374int cap_bprm_secureexec (struct linux_binprm *bprm) 371int cap_bprm_secureexec (struct linux_binprm *bprm)
@@ -448,7 +445,7 @@ static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
448{ 445{
449 if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) && 446 if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
450 (current->uid != 0 && current->euid != 0 && current->suid != 0) && 447 (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
451 !current->keep_capabilities) { 448 !issecure(SECURE_KEEP_CAPS)) {
452 cap_clear (current->cap_permitted); 449 cap_clear (current->cap_permitted);
453 cap_clear (current->cap_effective); 450 cap_clear (current->cap_effective);
454 } 451 }
@@ -547,7 +544,7 @@ int cap_task_setnice (struct task_struct *p, int nice)
547 * this task could get inconsistent info. There can be no 544 * this task could get inconsistent info. There can be no
548 * racing writer bc a task can only change its own caps. 545 * racing writer bc a task can only change its own caps.
549 */ 546 */
550long cap_prctl_drop(unsigned long cap) 547static long cap_prctl_drop(unsigned long cap)
551{ 548{
552 if (!capable(CAP_SETPCAP)) 549 if (!capable(CAP_SETPCAP))
553 return -EPERM; 550 return -EPERM;
@@ -556,6 +553,7 @@ long cap_prctl_drop(unsigned long cap)
556 cap_lower(current->cap_bset, cap); 553 cap_lower(current->cap_bset, cap);
557 return 0; 554 return 0;
558} 555}
556
559#else 557#else
560int cap_task_setscheduler (struct task_struct *p, int policy, 558int cap_task_setscheduler (struct task_struct *p, int policy,
561 struct sched_param *lp) 559 struct sched_param *lp)
@@ -572,12 +570,99 @@ int cap_task_setnice (struct task_struct *p, int nice)
572} 570}
573#endif 571#endif
574 572
573int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
574 unsigned long arg4, unsigned long arg5, long *rc_p)
575{
576 long error = 0;
577
578 switch (option) {
579 case PR_CAPBSET_READ:
580 if (!cap_valid(arg2))
581 error = -EINVAL;
582 else
583 error = !!cap_raised(current->cap_bset, arg2);
584 break;
585#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
586 case PR_CAPBSET_DROP:
587 error = cap_prctl_drop(arg2);
588 break;
589
590 /*
591 * The next four prctl's remain to assist with transitioning a
592 * system from legacy UID=0 based privilege (when filesystem
593 * capabilities are not in use) to a system using filesystem
594 * capabilities only - as the POSIX.1e draft intended.
595 *
596 * Note:
597 *
598 * PR_SET_SECUREBITS =
599 * issecure_mask(SECURE_KEEP_CAPS_LOCKED)
600 * | issecure_mask(SECURE_NOROOT)
601 * | issecure_mask(SECURE_NOROOT_LOCKED)
602 * | issecure_mask(SECURE_NO_SETUID_FIXUP)
603 * | issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)
604 *
605 * will ensure that the current process and all of its
606 * children will be locked into a pure
607 * capability-based-privilege environment.
608 */
609 case PR_SET_SECUREBITS:
610 if ((((current->securebits & SECURE_ALL_LOCKS) >> 1)
611 & (current->securebits ^ arg2)) /*[1]*/
612 || ((current->securebits & SECURE_ALL_LOCKS
613 & ~arg2)) /*[2]*/
614 || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
615 || (cap_capable(current, CAP_SETPCAP) != 0)) { /*[4]*/
616 /*
617 * [1] no changing of bits that are locked
618 * [2] no unlocking of locks
619 * [3] no setting of unsupported bits
620 * [4] doing anything requires privilege (go read about
621 * the "sendmail capabilities bug")
622 */
623 error = -EPERM; /* cannot change a locked bit */
624 } else {
625 current->securebits = arg2;
626 }
627 break;
628 case PR_GET_SECUREBITS:
629 error = current->securebits;
630 break;
631
632#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
633
634 case PR_GET_KEEPCAPS:
635 if (issecure(SECURE_KEEP_CAPS))
636 error = 1;
637 break;
638 case PR_SET_KEEPCAPS:
639 if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
640 error = -EINVAL;
641 else if (issecure(SECURE_KEEP_CAPS_LOCKED))
642 error = -EPERM;
643 else if (arg2)
644 current->securebits |= issecure_mask(SECURE_KEEP_CAPS);
645 else
646 current->securebits &=
647 ~issecure_mask(SECURE_KEEP_CAPS);
648 break;
649
650 default:
651 /* No functionality available - continue with default */
652 return 0;
653 }
654
655 /* Functionality provided */
656 *rc_p = error;
657 return 1;
658}
659
575void cap_task_reparent_to_init (struct task_struct *p) 660void cap_task_reparent_to_init (struct task_struct *p)
576{ 661{
577 cap_set_init_eff(p->cap_effective); 662 cap_set_init_eff(p->cap_effective);
578 cap_clear(p->cap_inheritable); 663 cap_clear(p->cap_inheritable);
579 cap_set_full(p->cap_permitted); 664 cap_set_full(p->cap_permitted);
580 p->keep_capabilities = 0; 665 p->securebits = SECUREBITS_DEFAULT;
581 return; 666 return;
582} 667}
583 668
diff --git a/security/dummy.c b/security/dummy.c
index b0232bbf427b..58d4dd1af5c7 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -604,7 +604,7 @@ static int dummy_task_kill (struct task_struct *p, struct siginfo *info,
604} 604}
605 605
606static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3, 606static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3,
607 unsigned long arg4, unsigned long arg5) 607 unsigned long arg4, unsigned long arg5, long *rc_p)
608{ 608{
609 return 0; 609 return 0;
610} 610}
diff --git a/security/security.c b/security/security.c
index 8a285c7b9962..d5cb5898d967 100644
--- a/security/security.c
+++ b/security/security.c
@@ -733,9 +733,9 @@ int security_task_wait(struct task_struct *p)
733} 733}
734 734
735int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, 735int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
736 unsigned long arg4, unsigned long arg5) 736 unsigned long arg4, unsigned long arg5, long *rc_p)
737{ 737{
738 return security_ops->task_prctl(option, arg2, arg3, arg4, arg5); 738 return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
739} 739}
740 740
741void security_task_reparent_to_init(struct task_struct *p) 741void security_task_reparent_to_init(struct task_struct *p)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 308e2cf17d75..04acb5af8317 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3303,12 +3303,13 @@ static int selinux_task_prctl(int option,
3303 unsigned long arg2, 3303 unsigned long arg2,
3304 unsigned long arg3, 3304 unsigned long arg3,
3305 unsigned long arg4, 3305 unsigned long arg4,
3306 unsigned long arg5) 3306 unsigned long arg5,
3307 long *rc_p)
3307{ 3308{
3308 /* The current prctl operations do not appear to require 3309 /* The current prctl operations do not appear to require
3309 any SELinux controls since they merely observe or modify 3310 any SELinux controls since they merely observe or modify
3310 the state of the current process. */ 3311 the state of the current process. */
3311 return 0; 3312 return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
3312} 3313}
3313 3314
3314static int selinux_task_wait(struct task_struct *p) 3315static int selinux_task_wait(struct task_struct *p)