Re: inheritable set [was Re: caps in elf headers: use the sticky

Y2K (y2k@y2ker.com)
Wed, 21 Apr 1999 13:39:09 -0700 (PDT)


On Mon, 19 Apr 1999 tytso@mit.edu wrote:
> Date: Sat, 17 Apr 1999 18:37:02 -0400 (EDT)
> From: Alexander Viro <viro@math.psu.edu>
> > You can do full POSIX capabilities and still be Unix; and the way you
> > solve this problem using model outlined by the POSIX capabilities draft
> No, because "more" wouldn't have any capabilities in its "inheritable"
> set, so it would NOT inherit any capabilities which its parents had.
Thats not very compatible, right now if the parent has the caps they flow
to their children. Actually for linux2.2.6 it appears that if ruid=0 or
euid=0 all caps are raised else all caps are droped upon exec.
> This is why it's important for programs to by default have a null
> inheritable set, and to only inherit capabilities if they have an
> explicitly set of privileges which they are allowed to inherit. This is
> an integral part of the POSIX capabilities model.
Then most of the executables would have to be trusted marked in order to
be backwards compatible. if a privledged process calls 'more' and has the
proper pP and pI set, why should it be incapable of passing those on?
The default fI should be pP and there should be an easy way for people
mark binaries to lose caps(elf headers work for me).
A smart caller will filter out what can be inheritied.
Dumb semi-legacy ones may leak a little for awhile, however it will be
less than having all_caps allways leaking with what happens with
pure-legacy root. non-root cap-enhanced are forced to raise pI if the want
their children to inherite those privledges.
I am developing a model that is fairly backwards compatible but able to
have fine-grained privledges.
To do so root is still special in very limited ways.
That is tweakable via the securebits.
Current root specialness:
1. suid root raises all caps in default compatible mode
Hopefully someone will make an better more finegrained method like
putting that info in ext3.
2. special cap_emulate_setxuid stuff used which has default capability
baggage associated with it.
3. euid root's children pI'=pI euid-non-root pI'=0
non-roots must explictly raise pI stuff if they want their children to
inherite anything.

root processes have been effectively weakened.
non-root cap-enhanced possible.
I need to track down the chmod syscall stuff and maybe put a little more
restrictions on what is needed to chmod +s on a root owned file.
I hope to have it work out to be fairly compatible for root-running
processes that maybe weakened. non-root cap-enhanced processes will be
forced to use a new api to accomplish some of the same things as a
legacy-root . ie. have their children inherite caps from them.
> The problem here is that some folks either don't understand the full
> POSIX capaibility model, or keep trying to leave parts of it out in the
> hopes of simplifying things. The reality is that you really do need to
> implement the full POSIX capability model for things to make sense.
The spec has been withdrawn. I might be taking quite a few liberties with
it -- I like the spec makes a good springboard. Maybe the latest patch
will indicate a little better what I have in my mind. Tell me what doesn't
make sense about it. BTW most of the elf header stuff originated from
pavel.

diff -ur linux/Documentation/Configure.help linux-cap/Documentation/Configure.help
--- linux/Documentation/Configure.help Wed Apr 21 00:15:55 1999
+++ linux-cap/Documentation/Configure.help Tue Apr 20 23:52:28 1999
@@ -1476,6 +1476,11 @@
called binfmt_elf.o. Saying M or N here is dangerous because some
crucial programs on your system might be in ELF format.

+Kernel support for ELF Capability Headers
+CONFIG_BINFMT_ELF_CAP_HEADER
+ Restrict Capabilities through the use of ELF Headers
+ A security related thingy
+
Kernel support for A.OUT binaries
CONFIG_BINFMT_AOUT
A.out (Assembler.OUTput) is a set of formats for libraries and
diff -ur linux/arch/i386/config.in linux-cap/arch/i386/config.in
--- linux/arch/i386/config.in Mon Feb 1 12:03:20 1999
+++ linux-cap/arch/i386/config.in Tue Apr 20 23:43:30 1999
@@ -86,6 +86,9 @@
bool 'Sysctl support' CONFIG_SYSCTL
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+if [ "$CONFIG_BINFMT_ELF" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'ELF Capability Header' CONFIG_BINFMT_ELF_CAP_HEADER
+fi
tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
diff -ur linux/fs/binfmt_elf.c linux-cap/fs/binfmt_elf.c
--- linux/fs/binfmt_elf.c Wed Apr 21 00:15:27 1999
+++ linux-cap/fs/binfmt_elf.c Wed Apr 21 08:51:50 1999
@@ -425,10 +425,22 @@

retval = -ENOEXEC;
/* First of all, some simple consistency checks */
- if (elf_ex.e_ident[0] != 0x7f ||
- strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0)
+ if (elf_ex.e_ident[0] != 0x7f)
goto out;

+#ifdef CONFIG_BINFMT_ELF_CAP_HEADER
+ if (strncmp(&elf_ex.e_ident[1], "ELF", 3) &&
+ strncmp(&elf_ex.e_ident[1], "FLE", 3))
+ goto out;
+#else /* CONFIG_BINFMT_ELF_CAP_HEADER */
+ if ( strncmp(&elf_ex.e_ident[1], "FLE", 3)==0) {
+ retval = -EPERM;
+ goto out;
+ }
+ if (strncmp(&elf_ex.e_ident[1], "ELF", 3) )
+ goto out;
+#endif /* CONFIG_BINFMT_ELF_CAP_HEADER */
+
if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
goto out;
if (!elf_check_arch(elf_ex.e_machine))
@@ -473,6 +485,47 @@
end_data = 0;

for (i = 0; i < elf_ex.e_phnum; i++) {
+#ifdef CONFIG_ELF_BINFMT_CAP_HEADER */
+ if (elf_ppnt->p_type == PT_NOTE) {
+ struct elf_capabilities_note note;
+
+ printk( "Before: " );
+ retval = read_exec(bprm->dentry, elf_ppnt->p_offset,
+ (void *) &note,
+ sizeof (struct elf_capabilities_note), 1);
+ if (retval<0)
+ goto out_free_ph;
+
+ printk( "(s) " );
+
+ if (note.note_signature != be32_to_cpu(0x43415053)) /* "CAPS" */
+ continue;
+
+ printk( "uid = %d, effective = %x, permitted = %x, inheritable = %x\n", bprm->e_uid, bprm->cap_effective, bprm->cap_permitted, bprm->cap_inheritable );
+
+ retval = -ENOEXEC;
+ if (note.cap.signature != 0xca5ab1e) {
+ printk( "signature = %x, version = %x, header @ %x\n", note.cap.signature, note.cap.version, elf_ppnt->p_offset );
+ goto out_free_ph;
+ }
+ if (note.cap.flags & ECF_MAKE_EUID_UID) /* You may want to loose owner's uid */
+ bprm->e_uid = current->uid;
+ if (!bprm->e_uid&&capable(CAP_SETUID) ) {
+ /* We only honour random uid changes for root with CAP_SETUID */
+ /* maybe we shouldn't care about CAP_SETUID here */
+ /* what is your opinion? */
+ if (note.cap.flags & ECF_MAKE_EUID_XUID)
+ bprm->e_uid = note.cap.xuid;
+ }
+ cap_mask( bprm->cap_effective, note.cap.effective );
+ cap_mask( bprm->cap_permitted, note.cap.permitted );
+ cap_mask( bprm->cap_inheritable, note.cap.inheritable );
+ /* bprm->securebits |= note.cap.securebits; */
+
+ printk( "Now: uid = %d, effective = %x, permitted = %x, inheritable = %x\n", bprm->e_uid, bprm->cap_effective, bprm->cap_permitted, bprm->cap_inheritable );
+
+ }
+#endif /* CONFIG_BINFMT_ELF_CAP_HEADER */
if (elf_ppnt->p_type == PT_INTERP) {
retval = -EINVAL;
if (elf_interpreter)
diff -ur linux/fs/exec.c linux-cap/fs/exec.c
--- linux/fs/exec.c Mon Jan 18 13:47:38 1999
+++ linux-cap/fs/exec.c Wed Apr 21 12:22:33 1999
@@ -580,6 +580,7 @@

bprm->e_uid = current->euid;
bprm->e_gid = current->egid;
+ /* bprm->securebits = current->securebits | securebits */
id_change = cap_raised = 0;

/* Set-uid? */
@@ -602,28 +603,38 @@
}

/* We don't have VFS support for capabilities yet */
- cap_clear(bprm->cap_inheritable);
+ /* if we did we'd do something like this pseudo */
+ /* if (HAD_VFS_CAPS_AVAIL) {
+ * USE_VFS_CAPS
+ * else {
+ * USE_DEFAULTS_AS_BELOW
+ * }
+ */
+
+ /* these are some useful defaults */
+ cap_t(bprm->cap_inheritable)=cap_t(current->cap_permitted);
cap_clear(bprm->cap_permitted);
- cap_clear(bprm->cap_effective);
+ cap_set_full(bprm->cap_effective);

/* To support inheritance of root-permissions and suid-root
* executables under compatibility mode, we raise the
- * effective and inherited bitmasks of the executable file
+ * effective and permitted bitmasks of suid-root executable files
* (translation: we set the executable "capability dumb" and
- * set the allowed set to maximum). We don't set any forced
- * bits.
+ * set the allowed set to maximum).
*
- * If only the real uid is 0, we only raise the inheritable
- * bitmask of the executable file (translation: we set the
- * allowed set to maximum and the application to "capability
- * smart").
+ * if root executes a non-root-suid file he will not raise
+ * any special privledges. He will however have his effective
+ * set cleared out for backwards compatibility.
*/

- if (!issecure(SECURE_NOROOT)) {
- if (bprm->e_uid == 0 || current->uid == 0)
- cap_set_full(bprm->cap_inheritable);
- if (bprm->e_uid == 0)
+ if (!issecure(SECURE_NOROOT)&&(mode&S_ISUID) ) {
+ if (inode->i_uid==0) {
+ cap_set_full(bprm->cap_permitted);
cap_set_full(bprm->cap_effective);
+ }
+ else if (current->uid==0) {
+ cap_clear(bprm->cap_effective);
+ }
}

/* Only if pP' is _not_ a subset of pP, do we consider there
@@ -668,7 +679,8 @@
* The formula used for evolving capabilities is:
*
* pI' = pI
- * (***) pP' = fP | (fI & pI)
+ * (***) pP' = fP | (fI & pI & pP)
+ * used to be pP' = fP | (fI & pI )
* pE' = pP' & fE [NB. fE is 0 or ~0]
*
* I=Inheritable, P=Permitted, E=Effective // p=process, f=file
@@ -679,7 +691,8 @@
{
int new_permitted = cap_t(bprm->cap_permitted) |
(cap_t(bprm->cap_inheritable) &
- cap_t(current->cap_inheritable));
+ cap_t(current->cap_inheritable) &
+ cap_t(current->cap_permitted) );

/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
@@ -694,6 +707,9 @@

current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ /* current->securebits=bprm->securebits | securebits */
+ if (!issecure(SECURE_NOROOT)&&current->euid)
+ cap_clear(current->cap_inheritable);
if (current->euid != current->uid || current->egid != current->gid ||
!cap_issubset(new_permitted, current->cap_permitted))
current->dumpable = 0;
diff -ur linux/include/linux/elf.h linux-cap/include/linux/elf.h
--- linux/include/linux/elf.h Tue Jan 26 15:21:22 1999
+++ linux-cap/include/linux/elf.h Wed Apr 21 08:16:47 1999
@@ -496,6 +496,41 @@
Elf32_Word n_type; /* Content type */
} Elf64_Nhdr;

+/* Capabilities support
+ */
+struct elf_capabilities {
+ Elf32_Word signature;
+ Elf32_Word version; /* Currently 0, this is so that you can append on the end painlessly */
+ Elf32_Word flags;
+#define ECF_MAKE_EUID_UID 1
+#define ECF_MAKE_EUID_XUID 2
+ Elf32_Word xuid;
+ Elf32_Word securebits;
+ /* hopefully some day we can have per process issecure() */
+ Elf32_Word effective;
+ Elf32_Word effective1;
+ Elf32_Word effective2;
+ Elf32_Word effective3;
+ Elf32_Word permitted;
+ Elf32_Word permitted1;
+ Elf32_Word permitted2;
+ Elf32_Word permitted3;
+ Elf32_Word inheritable;
+ Elf32_Word inheritable1;
+ Elf32_Word inheritable2;
+ Elf32_Word inheritable3;
+ Elf32_Word known;
+ Elf32_Word known1;
+ Elf32_Word known2;
+ Elf32_Word known3;
+};
+
+struct elf_capabilities_note {
+ Elf32_Nhdr notehdr;
+ __u32 note_signature; /* == "CAPS" */
+ struct elf_capabilities cap;
+};
+
#if ELF_CLASS == ELFCLASS32

extern Elf32_Dyn _DYNAMIC [];
diff -ur linux/include/linux/securebits.h linux-cap/include/linux/securebits.h
--- linux/include/linux/securebits.h Wed Apr 1 16:26:34 1998
+++ linux-cap/include/linux/securebits.h Wed Apr 21 00:27:01 1999
@@ -23,8 +23,16 @@
setting is fixed or not. A setting which is fixed cannot be changed
from user-level. */

+#if 1
#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? \
(1 << (X)) & SECUREBITS_DEFAULT : \
(1 << (X)) & securebits )
+#else
+#define issecure(X) ( (1 << (X+1)) & SECUREBITS_DEFAULT ? \
+ (1 << (X)) & SECUREBITS_DEFAULT : \
+ (1 << (X)) & current->securebits )
+/* there is currently no perprocess securebits defined */
+/* would have do some thing like current->securebits=securebits when exec */
+#endif

#endif /* !_LINUX_SECUREBITS_H */
diff -ur linux/kernel/sys.c linux-cap/kernel/sys.c
--- linux/kernel/sys.c Fri Nov 20 11:43:19 1998
+++ linux-cap/kernel/sys.c Wed Apr 21 12:48:11 1999
@@ -318,6 +318,7 @@
*
* 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
* capabilities are set to the permitted capabilities.
+ * The inheritible set is combined with the permitted.
*
* fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
* never happen.
@@ -337,6 +338,7 @@
}
if (old_euid != 0 && current->euid == 0) {
current->cap_effective = current->cap_permitted;
+ cap_combine(current->cap_inheritable,current->cap_permitted);
}
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/