Serge et al.,

I believe the attached patch addresses your concerns.

Cheers

Andrew

Serge E. Hallyn wrote:
> How about just defining CAP_BSET_INIT once in include/linux/capability.h
> depending on CONFIG_SECURITY_FILE_CAPABILITIES, and at
> kernel/capability.c changing 
>       kernel_cap_t cap_bset = CAP_INIT_EFF_SET;
> to
>       kernel_cap_t cap_bset = CAP_BSET_INIT;
> ?
> 
> Rather than having it be set in two different places.
[..]
> Looks functionally correct, though I'd much rather see something like:
> 
> #ifdef CONFIG_SECURITY_FILE_CAPABILITIES
> static inline int cap_may_setcap(struct task_struct *t)
[..]
>From 4ea98a9ff124696ffb8ff9dab59a8568c7cfb281 Mon Sep 17 00:00:00 2001
From: Andrew Morgan <[EMAIL PROTECTED]>
Date: Mon, 6 Aug 2007 22:53:08 -0700
Subject: [PATCH] =?utf-8?q?Change=C2=A0the=C2=A0meaning=C2=A0of=C2=A0CAP=5FSETPCAP=C2=A0when=C2=A0filesystem=C2=A0support=C2=A0is=C2=A0enabled.?=
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

The non-filesystem capability meaning of CAP_SETPCAP is that a
process, p1, can change the capabilities of another process, p2. This
is not the meaning that was intended for this capability at all, and
this implementation came about purely because, without filesystem
capabilities, there was no way to use capabilities without one process
bestowing them on another.

Since we now have a filesystem support for capabilities we can fix the
implementation of CAP_SETPCAP.

The most significant thing about this change is that, with it in
effect, no process can set the capabilities of another process.

The capabilities of a program are set via the capability convolution
rules:

   pI(post-exec) = pI(pre-exec)
   pP(post-exec) = (X(aka cap_bset) & fP) | (pI(post-exec) & fI)
   pE(post-exec) = fE ? pP(post-exec) : 0

at exec() time. As such, the only influence the pre-exec() program can
have on the post-exec() program's capabilities are throught the pI
capability set.

The correct implementation for CAP_SETPCAP (and that enabled by this
patch) is that it can be used to add extra pI capabilities to the
current process - to be picked up by subsequent exec()s when the above
convolution rules are applied.

Here is how it works:

Let's say we have a process, p. It has capability sets, pE, pP and pI.
Generally, p, can change the value of its own pI to pI' where

   (pI' & ~pI) & ~pP = 0.

That is, the only new things in pI' that were not present in pI need to
be present in pP.

The role of CAP_SETPCAP is basically to permit changes to pI beyond
the above:

   if (pE & CAP_SETPCAP) {
      pI' = anything; /* ie., even (pI' & ~pI) & ~pP != 0  */
   }

This capability is useful for things like login, which (say, via
pam_cap) might want to raise certain inheritable capabilities for use
by the children of the logged-in user's shell, but those capabilities
are not useful to or needed by the login program itself.

One such use might be to limit who can run ping. You set the
capabilities of the 'ping' program to be "= cap_net_raw+i", and then
only shells that have (pI & CAP_NET_RAW) will be able to run
it. Without CAP_SETPCAP implemented as described above, login(pam_cap)
would have to also have (pP & CAP_NET_RAW) in order to raise this
capability and pass it on through the inheritable set.

Signed-off-by: Andrew Morgan <[EMAIL PROTECTED]>
---
 include/linux/capability.h |    4 ---
 include/linux/security.h   |    6 ++++
 kernel/capability.c        |    8 +-----
 kernel/sysctl.c            |    3 +-
 security/commoncap.c       |   57 ++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 8961e7f..7a8d7ad 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -310,10 +310,6 @@ typedef __u32 kernel_cap_t;
 #define CAP_SETFCAP	     31
 
 #ifdef __KERNEL__
-/*
- * Bounding set
- */
-extern kernel_cap_t cap_bset;
 
 /*
  * Internal kernel functions only
diff --git a/include/linux/security.h b/include/linux/security.h
index a9d3f31..1b15a5f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -34,6 +34,11 @@
 #include <linux/xfrm.h>
 #include <net/flow.h>
 
+/*
+ * Bounding set
+ */
+extern kernel_cap_t cap_bset;
+
 struct ctl_table;
 
 /*
@@ -1405,6 +1410,7 @@ struct security_operations {
 };
 
 /* global variables */
+
 extern struct security_operations *security_ops;
 
 /* inline stuff */
diff --git a/kernel/capability.c b/kernel/capability.c
index c8d3c77..eea8768 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1997  Andrew Main <[EMAIL PROTECTED]>
  *
- * Integrated into 2.1.97+,  Andrew G. Morgan <[EMAIL PROTECTED]>
+ * Integrated into 2.1.97+,  Andrew G. Morgan <[EMAIL PROTECTED]>
  * 30 May 2002:	Cleanup, Robert M. Love <[EMAIL PROTECTED]>
  */ 
 
@@ -14,12 +14,6 @@
 #include <linux/syscalls.h>
 #include <asm/uaccess.h>
 
-unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
-kernel_cap_t cap_bset = CAP_INIT_EFF_SET;
-
-EXPORT_SYMBOL(securebits);
-EXPORT_SYMBOL(cap_bset);
-
 /*
  * This lock protects task->cap_* for all tasks including current.
  * Locking rule: acquire this prior to tasklist_lock.
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 79c891e..acabeb4 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -24,10 +24,9 @@
 #include <linux/slab.h>
 #include <linux/sysctl.h>
 #include <linux/proc_fs.h>
-#include <linux/capability.h>
+#include <linux/security.h>
 #include <linux/ctype.h>
 #include <linux/utsname.h>
-#include <linux/capability.h>
 #include <linux/smp_lock.h>
 #include <linux/fs.h>
 #include <linux/init.h>
diff --git a/security/commoncap.c b/security/commoncap.c
index 6349093..0f15b76 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -24,6 +24,23 @@
 #include <linux/hugetlb.h>
 #include <linux/mount.h>
 
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+/*
+ * Because of the reduced scope of CAP_SETPCAP when filesystem
+ * capabilities are in effect, it is safe to allow this capability to
+ * be available in the default configuration.
+ */
+# define CAP_INIT_BSET  CAP_FULL_SET
+#else /* ie. ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+# define CAP_INIT_BSET  CAP_INIT_EFF_SET
+#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
+
+unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
+EXPORT_SYMBOL(securebits);
+
+kernel_cap_t cap_bset = CAP_INIT_BSET;    /* systemwide capability bound */
+EXPORT_SYMBOL(cap_bset);
+
 int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
 	NETLINK_CB(skb).eff_cap = current->cap_effective;
@@ -75,14 +92,44 @@ int cap_capget (struct task_struct *target, kernel_cap_t *effective,
 	return 0;
 }
 
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+
+static inline int cap_block_setpcap(struct task_struct *target)
+{
+	/*
+	 * No support for remote process capability manipulation with
+	 * filesystem capability support.
+	 */
+	return (target != current);
+}
+
+static inline int cap_inh_is_capped(void)
+{
+	/*
+	 * return 1 if changes to the inheritable set are limited
+	 * to the old permitted set.
+	 */
+	return !cap_capable(current, CAP_SETPCAP);
+}
+
+#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+
+static inline int cap_block_setpcap(struct task_struct *t) { return 0; }
+static inline int cap_inh_is_capped(void) { return 1; }
+
+#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
+
 int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
 		      kernel_cap_t *inheritable, kernel_cap_t *permitted)
 {
-	/* Derived from kernel/capability.c:sys_capset. */
-	/* verify restrictions on target's new Inheritable set */
-	if (!cap_issubset (*inheritable,
-			   cap_combine (target->cap_inheritable,
-					current->cap_permitted))) {
+	if (cap_block_setpcap(target)) {
+		return -EPERM;
+	}
+	if (cap_inh_is_capped()
+	    && !cap_issubset(*inheritable,
+			     cap_combine(target->cap_inheritable,
+					 current->cap_permitted))) {
+		/* incapable of using this inheritable set */
 		return -EPERM;
 	}
 
-- 
1.5.1.3

Reply via email to