linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] + story;) on POSIX capabilities and SUID bit
@ 2002-04-12 18:38 Marek Zelem
  2002-04-16  2:15 ` David Wagner
  0 siblings, 1 reply; 3+ messages in thread
From: Marek Zelem @ 2002-04-12 18:38 UTC (permalink / raw)
  To: linux-kernel


	Hello

The POSIX capabilities support defines the file and process capabilities,
and rules to be obeyed at particular events, such as exec() and so.
Because of historic reasons, there is also a SUID bit, which is also
POSIX, and has its own set of rules. You can choose either to use
SUID, or to use POSIX capabilities. If you want to be compatible with
both of them, you have a problem.

Linux internally uses POSIX capabilities to verify permissions. On
the other hand, it uses a filesystem, which doesn't hold the capability
information at each file, so it must use SUID bit, which is available
on all UNIX-like filesystems. This is called the SUID emulation, and
if everything goes well, it might disappear some day and will be replaced
by proper implementation of capabilities in the filesystem.

The rules for computing new process capabilities at exec() involve
the previous set of process capabilities, and the capabilities got
from the file to be executed. (I,P and E means inherited, permitted
and effective capabilities, p means process caps and f means file caps).

The old formula, which is used during exec:
  *       pI' = pI
  * (***) pP' = (fP & X) | (fI & pI)
  *       pE' = pP' & fE          [NB. fE is 0 or ~0]
is not compatible with the SUID-bit semantics. Because of this, there is
an ugly piece of code in prepare_binprm(), which (after several lines
of code) results in setting either FULL or NONE capabilities to the
process. If we want to have the POSIX capabilities working, we
DON'T want this behaviour. We want to have new capabilities reflecting
the old ones and, at the same time, honour the SUID bit.

Our new formula:
  * (***) pP' = (fP & X) | (fI & pI)
  *       pI' = pP'
  *       pE' = ((pP' & pE) | fP) & X & fE
respects the ideas of POSIX capabilities, and preserves the SUID bit emulation
at the same time. In this case, SUID bit just alters the file capabilities.

The new formula is not exactly the same as in the POSIX standard. That's o.k.
The old version, with SUID bit emulation turned on, was in conflict with
POSIX too. The new one honours the SUID bit just like the previous one,
and ENABLES use of the file capabilities. Thus, the new implementation
  * is ready for the POSIX caps-enabled filesystems and
  * enables use of POSIX capabilities even in the system, which uses
    the SUID files.

					Marek Zelem

Here follows the patch for 2.5. For those who are interrested, there
is a 2.4 patch available at
	http://www.terminus.sk/~marek/kernel/capabilities-2.4.18.diff.

--- linux-2.5.7.orig/fs/exec.c	Mon Mar 18 21:37:12 2002
+++ linux-2.5.7/fs/exec.c	Fri Apr 12 19:29:53 2002
@@ -20,6 +20,10 @@
  * table to check for several different types  of binary formats.  We keep
  * trying until we recognize the file or we run out of supported binary
  * formats.
+ *
+ * Modified formula for evolving capabilities to allow nice SUID emulation
+ * which work together with (future) VFS capabilities implementation.
+ * Feb 2002 by Marek Zelem <marek@terminus.sk>
  */

 #include <linux/config.h>
@@ -651,11 +655,23 @@
 	bprm->e_uid = current->euid;
 	bprm->e_gid = current->egid;

+	/* We don't have VFS support for capabilities yet */
+	cap_clear(bprm->cap_permitted);
+	cap_set_full(bprm->cap_inheritable);
+	cap_set_full(bprm->cap_effective);
+
 	if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
 		/* Set-uid? */
-		if (mode & S_ISUID)
+		if (mode & S_ISUID) {
 			bprm->e_uid = inode->i_uid;
-
+			if (bprm->e_uid == 0) {
+				if (!issecure(SECURE_NOROOT))
+					cap_set_full(bprm->cap_permitted);
+			}
+			else {
+				cap_clear(bprm->cap_effective);
+			}
+		}
 		/* Set-gid? */
 		/*
 		 * If setgid is set but no group execute bit then this
@@ -666,28 +682,6 @@
 			bprm->e_gid = inode->i_gid;
 	}

-	/* We don't have VFS support for capabilities yet */
-	cap_clear(bprm->cap_inheritable);
-	cap_clear(bprm->cap_permitted);
-	cap_clear(bprm->cap_effective);
-
-	/*  To support inheritance of root-permissions and suid-root
-         *  executables under compatibility mode, we raise all three
-         *  capability sets for the file.
-         *
-         *  If only the real uid is 0, we only raise the inheritable
-         *  and permitted sets of the executable file.
-         */
-
-	if (!issecure(SECURE_NOROOT)) {
-		if (bprm->e_uid == 0 || current->uid == 0) {
-			cap_set_full(bprm->cap_inheritable);
-			cap_set_full(bprm->cap_permitted);
-		}
-		if (bprm->e_uid == 0)
-			cap_set_full(bprm->cap_effective);
-	}
-
 	memset(bprm->buf,0,BINPRM_BUF_SIZE);
 	return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
 }
@@ -698,6 +692,11 @@
  *
  * The formula used for evolving capabilities is:
  *
+ * (***) pP' = (fP & X) | (fI & pI)
+ *       pI' = pP'
+ *       pE' = ((pP' & pE) | fP) & X & fE
+ *
+ * original was:
  *       pI' = pI
  * (***) pP' = (fP & X) | (fI & pI)
  *       pE' = pP' & fE          [NB. fE is 0 or ~0]
@@ -744,6 +743,14 @@
          * capability rules */
 	if (current->pid != 1) {
 		current->cap_permitted = new_permitted;
+/* This is to allow good SUID emulation (Marek Zelem <marek@terminus.sk>) */
+		current->cap_inheritable = new_permitted;
+		working =
+			cap_intersect(new_permitted,current->cap_effective);
+		new_permitted =
+			cap_combine(working,bprm->cap_permitted);
+		new_permitted = cap_intersect(new_permitted, cap_bset);
+/* END of good SUID emulation (Marek Zelem <marek@terminus.sk>) */
 		current->cap_effective =
 			cap_intersect(new_permitted, bprm->cap_effective);
 	}
--- linux-2.5.7.orig/include/linux/capability.h	Mon Mar 18 21:37:09 2002
+++ linux-2.5.7/include/linux/capability.h	Fri Apr 12 19:29:53 2002
@@ -302,8 +302,9 @@

 #define CAP_EMPTY_SET       to_cap_t(0)
 #define CAP_FULL_SET        to_cap_t(~0)
-#define CAP_INIT_EFF_SET    to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
-#define CAP_INIT_INH_SET    to_cap_t(0)
+//#define CAP_INIT_EFF_SET    to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP))
+#define CAP_INIT_EFF_SET    to_cap_t(~0)
+#define CAP_INIT_INH_SET    to_cap_t(~0)

 #define CAP_TO_MASK(x) (1 << (x))
 #define cap_raise(c, flag)   (cap_t(c) |=  CAP_TO_MASK(flag))
--- linux-2.5.7.orig/kernel/sys.c	Mon Mar 18 21:37:05 2002
+++ linux-2.5.7/kernel/sys.c	Fri Apr 12 19:29:53 2002
@@ -482,6 +482,7 @@
 	    !current->keep_capabilities) {
 		cap_clear(current->cap_permitted);
 		cap_clear(current->cap_effective);
+		cap_clear(current->cap_inheritable);
 	}
 	if (old_euid == 0 && current->euid != 0) {
 		cap_clear(current->cap_effective);
--- linux-2.5.7.orig/kernel/kmod.c	Mon Mar 18 21:37:10 2002
+++ linux-2.5.7/kernel/kmod.c	Fri Apr 12 19:29:53 2002
@@ -134,6 +134,8 @@
 	curtask->euid = curtask->fsuid = 0;
 	curtask->egid = curtask->fsgid = 0;
 	cap_set_full(curtask->cap_effective);
+	cap_set_full(curtask->cap_inheritable);
+	cap_set_full(curtask->cap_permitted);

 	/* Allow execve args to be in kernel space. */
 	set_fs(KERNEL_DS);


--
  e-mail: marek@terminus.sk
  web: http://www.terminus.sk/~marek/
  pgp key: http://www.terminus.sk/~marek/gpg.txt



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] + story;) on POSIX capabilities and SUID bit
  2002-04-12 18:38 [PATCH] + story;) on POSIX capabilities and SUID bit Marek Zelem
@ 2002-04-16  2:15 ` David Wagner
  2002-04-17 16:06   ` Marek Zelem
  0 siblings, 1 reply; 3+ messages in thread
From: David Wagner @ 2002-04-16  2:15 UTC (permalink / raw)
  To: linux-kernel

Marek Zelem  wrote:
>Our new formula:
>  * (***) pP' = (fP & X) | (fI & pI)
>  *       pI' = pP'
>  *       pE' = ((pP' & pE) | fP) & X & fE

Can you say anything about why this is safe and doesn't introduce
vulnerabilities?  (The capabilities misfeature that caused sendmail
8.10.1 to leak root privilege really drove home for me the subtlety of
this stuff.)

Also, the meaning of fE and fP seem backwards from what I would have
expected.  Maybe this reflects a lack in my understanding in capabilities,
but I thought 'effective' refers to capabilities you're allowed to invoke
at the moment, whereas 'permitted' refers to an upper bound on what
capabilities you're allowed enable in 'effective', consequently I would
have swapped the treatment of fE and fP.  Can you clear up my confusion?

Finally, what's the story behind the changes to CAP_INIT_EFF_SET and
CAP_INIT_INH_SET, and the business with CAP_SETPCAP?  If I understand
correctly, one side-effect of this change is that you've changed cap_bset
(X, the global bound on capabilities above) to add CAP_SETPCAP to it.
Is this safe?  What motivated this change?  Did I understand correctly?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] + story;) on POSIX capabilities and SUID bit
  2002-04-16  2:15 ` David Wagner
@ 2002-04-17 16:06   ` Marek Zelem
  0 siblings, 0 replies; 3+ messages in thread
From: Marek Zelem @ 2002-04-17 16:06 UTC (permalink / raw)
  To: linux-kernel


On 16 Apr 2002, David Wagner wrote:

> Marek Zelem  wrote:
> >Our new formula:
> >  * (***) pP' = (fP & X) | (fI & pI)
> >  *       pI' = pP'
> >  *       pE' = ((pP' & pE) | fP) & X & fE
>
> Can you say anything about why this is safe and doesn't introduce
> vulnerabilities?  (The capabilities misfeature that caused sendmail
> 8.10.1 to leak root privilege really drove home for me the subtlety of
> this stuff.)

New 'permitted' capabilities for process are computed by the same formula as
the original because there is the primary aspiration to preserve the original
POSIX formula.
Most significat difference is in computation of 'inheritable' capabilities.
Original formula just keeps process 'inheritable' capabilities untouched.
(This fact was major problem of consolidate POSIX capabilities and SUID bit.)
New formula set the 'inheritable' capabilities to correspond with the new
'permitted'.
There can occur four situations:
	1) capability was in pI and will be in pI' - this correspond with
		the original formula
	2) capability wasn't in pI and will not be in pI' - this also
		correspond with the original formula
	3) capability was in pI and will not be in pI' - (so is not set
		in new pP) - meaning: proces is NOT able to raise self pP by
		executing "nosuid" binary. In this case is new formula
		more restrictive then original.
	4) capability wasn't in pI and will be in pI' - this can occure
		only if this capability is in fP. In this case this
		capability is also in pP'. And if is in 'permitted' caps
		then process is allowed to add this capability into
		'inheritable' caps. So we do nothin wrong if we set this
		capability into pI'.
Finally, difference in computation of pE' only aspirate to reflect pE.
Major principle was preserved: pE' = pP' & fE. In new formula was pP'
replaced by more complex term. But it's always true that
	(((pP' & pE) | fP) & X & fE) IS SUBSET of (pP' & fE).
So I think this is safe.

>
> Also, the meaning of fE and fP seem backwards from what I would have
> expected.  Maybe this reflects a lack in my understanding in capabilities,
> but I thought 'effective' refers to capabilities you're allowed to invoke
> at the moment, whereas 'permitted' refers to an upper bound on what
> capabilities you're allowed enable in 'effective', consequently I would
> have swapped the treatment of fE and fP.  Can you clear up my confusion?

File capabilities are little different from process capabilities. Meaninig
of file capabilities is:
	fP - capafilities which are "forced" to process by exec().
	     (sometimes called 'forced' capabilities)
	fI - capabilities which is "allowed" to remain in process 'permited'.
	     (sometimes called 'allowed' capabilities)
	fE - capabilities which select which of 'permited' will be
	     initially also 'effective'. (afrer exec() of course.)

>
> Finally, what's the story behind the changes to CAP_INIT_EFF_SET and
> CAP_INIT_INH_SET, and the business with CAP_SETPCAP?  If I understand
> correctly, one side-effect of this change is that you've changed cap_bset
> (X, the global bound on capabilities above) to add CAP_SETPCAP to it.
> Is this safe?  What motivated this change?  Did I understand correctly?

Yes, you understand correctly. Enabling CAP_SETPCAP is safe exactly as
enabling the CAP_SYS_MODULE, which is enabled by default. Capability
CAP_SYS_MODULE allows process to modify the cap_bset. Maybe I am wrong,
but my opinion is not to do inconsequential restrictions. So, this is
reason why I decide to change CAP_INIT_EFF_SET.

					Marek Zelem
--
  e-mail: marek@terminus.sk
  web: http://www.terminus.sk/~marek/
  pgp key: http://www.terminus.sk/~marek/gpg.txt





^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2002-04-17 16:02 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-04-12 18:38 [PATCH] + story;) on POSIX capabilities and SUID bit Marek Zelem
2002-04-16  2:15 ` David Wagner
2002-04-17 16:06   ` Marek Zelem

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).