Capabilities for elf executables under Linux

Pavel Machek (pavel@bug.ucw.cz)
Wed, 2 Jun 1999 23:01:26 +0200


Hi!

This is next try with capabilities (improved docs, ported to
2.3.4; should work ok with 2.2.9). Why do I think this is a good idea?

* it immediately gives us better security: you can for example easily
say "I want ping to run with CAP_NET_RAW raised, but nothing more",
and it is possible even without ping being recompiled

* it brings otherwise-unused capabilities mechanism into live, so that
it can be tested and improved

Yes, I know it is elf specific, but most setuid0 applications at your
system are elf, anyways. I believe that even in future most
priviledged applications will stay to be elf, and this will be
preffered way for storing capabilities information on media like cdrom
(where you want to keep iso9660 format).

Also, I expect userland problems. [For example current dynamic linker
will look if euid==ruid, and if they are equal, it will honour
LD_PRELOAD. This would screw up capabilities system in big way.]
Therefore it is probably good idea to start using capabilities _now_
so that userland has more time (till end of 2.3 series) to find stupid
problems.

I believe this is very good now, and it will be usable even in future
because iso9660-like formats are here to stay. Please apply,

Pavel

--- /dev/null Tue Jul 21 02:45:36 1998
+++ linux/Documentation/capabilities.txt Wed Jun 2 22:46:58 1999
@@ -0,0 +1,52 @@
+
+Elf capabilities hack
+
+ From now on, there's support for capabilities in elf executable. Elf
+ executable now may contain "capabilities header", telling which
+ capabilities should be dropped on exec. This can not hurt: lowering
+ capabilities is not priviledged operation, and executable could do it
+ itself at beggining of main.
+
+ Doing it in exec() time has certain advantages, through: you can
+ easily look and what capabilities are in use by what program and you
+ can set capabilities for existing executables without need to
+ recompile. (It is hard to create tool which insers elfcap header into
+ elf file. But it has been done. Inserting code to drop capabilities on
+ the beggining of main would be nightmare.)
+
+ Notice that this system is very nice, but as described has limited
+ use. It only lowers capabilities, and raising capabilities is what
+ causes problems. (50% of security holes in unix are related to setuid0
+ programs). But wait: elfcap can easily be used to limit damage done by
+ setuid0 programs. It needs only little trick: ability to set euid back
+ to ruid. By setuid0, process gets all capabilities, and elfcap is free
+ to drop that capabilities it does not want.
+
+ So, along with existing setuid mechanism, this hack can be used to
+ grant subset of capabilities to executables. For example currently
+ ping has to be setuid0. With elfcap, ping still will be setuid0, but
+ most of its capabilities (and its euid) will be dropped at exec()
+ time, so breaking into ping will allow attacker to generate arbitrary
+ packets to network, but nothing more.
+
+ Summary of what can elfcap do:
+ * mask inheritable, permitted and effective sets by arbitrary mask
+ * set euid back to ruid (effectivelly undoing result of setuid bit)
+ Warning: using this feature with unpatched dynamic linker is
+ security hole: current dynamic linker will test and if (euid==uid)
+ will honour LD_PRELOAD, leaking capabilities to anyone.
+ * set euid to arbitrary value if euid==0
+
+ For more info & utility programs, look at
+ http://atrey.karlin.mff.cuni.cz/~pavel/elfcap.html.
+
+Bad idea
+
+ I should empatize that it is bad idea to give suid0 to any program
+ just because you have capabilities. If program did not have suid0
+ yesterday, it probably should not have suid0 today. (Think about
+ booting old kernel, for example).
+
+ Pavel Machek
+ <pavel@ucw.cz>
+
\ No newline at end of file
--- clean//fs/binfmt_elf.c Wed Jun 2 14:10:26 1999
+++ linux/fs/binfmt_elf.c Wed Jun 2 11:36:41 1999
@@ -7,6 +7,7 @@
* Tools".
*
* Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
+ * Capabilities copyright 1999 Pavel Machek (pavel@ucw.cz).
*/

#include <linux/module.h>
@@ -387,6 +388,26 @@
return elf_entry;
}

+static void
+restrict( struct linux_binprm * bprm, struct elf_capabilities *cap )
+{
+ if (cap->signature != 0xca5ab1e)
+ return;
+
+ /* I do not check versions... That is because current version
+ is 0 and I expect all changes to be backward - compabtible */
+ if (cap->flags & ECF_MAKE_EUID_UID) /* You may want to loose owner's uid */
+ bprm->e_uid = current->uid;
+ if ((!bprm->e_uid) && (cap->flags & ECF_MAKE_EUID_XUID))
+ bprm->e_uid = cap->xuid; /* We only honour random uid changes for root */
+ cap_mask( bprm->cap_effective, cap->effective );
+ cap_mask( bprm->cap_permitted, cap->permitted );
+ cap_mask( bprm->cap_inheritable, cap->inheritable );
+
+ printk( KERN_DEBUG "Now: uid = %d, effective = %x, permitted = %x, inheritable = %x\n", bprm->e_uid, bprm->cap_effective, bprm->cap_permitted, bprm->cap_inheritable );
+}
+
+
/*
* These are the functions used to load ELF style executables and shared
* libraries. There is no binary dependent code anywhere else.
@@ -396,6 +417,7 @@
#define INTERPRETER_AOUT 1
#define INTERPRETER_ELF 2

+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))

static inline int
do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
@@ -473,6 +498,22 @@
end_data = 0;

for (i = 0; i < elf_ex.e_phnum; i++) {
+ if (elf_ppnt->p_type == PT_NOTE) {
+ struct elf_capabilities_note note;
+ int offset = elf_ppnt->p_offset;
+ int maxoffset = offset + elf_ppnt->p_filesz;
+
+ while (offset <= (maxoffset - sizeof(note))) {
+ int retval;
+ retval = read_exec(bprm->dentry, offset, (void *) &note,
+ sizeof(note), 1);
+ if (retval != sizeof(note))
+ goto skip;
+ if (note.note_signature == be32_to_cpu(0x43415053)) /* "CAPS" */
+ restrict(bprm, &note.cap);
+ offset += sizeof(Elf32_Nhdr) + roundup(note.nhdr.n_namesz, 4) + roundup(note.nhdr.n_descsz, 4);
+ }
+ }
if (elf_ppnt->p_type == PT_INTERP) {
retval = -EINVAL;
if (elf_interpreter)
@@ -533,6 +574,7 @@
interp_ex = *((struct exec *) bprm->buf);
interp_elf_ex = *((struct elfhdr *) bprm->buf);
}
+ skip:
elf_ppnt++;
}

--- clean//include/linux/elf.h Thu Jun 25 17:38:14 1998
+++ linux/include/linux/elf.h Tue May 18 22:34:21 1999
@@ -496,6 +496,27 @@
Elf32_Word n_type; /* Content type */
} Elf64_Nhdr;

+/* Capabilities support
+ */
+struct elf_capabilities {
+ __u32 signature;
+ __u32 version; /* Currently 0; this is so that you can append on the end painlessly */
+ __u32 flags;
+#define ECF_MAKE_EUID_UID 1
+#define ECF_MAKE_EUID_XUID 2
+ __u32 xuid; /* We want our set 128bit for future expansion */
+ __u32 effective, effective1, effective2, effective3;
+ __u32 permitted, permitted1, permitted2, permitted3;
+ __u32 inheritable, inheritable1, inheritable2, inheritable3;
+ __u32 known, known1, known2, known3;
+};
+
+struct elf_capabilities_note {
+ Elf32_Nhdr nhdr;
+ __u32 note_signature; /* == "CAPS" */
+ struct elf_capabilities cap;
+};
+
#if ELF_CLASS == ELFCLASS32

extern Elf32_Dyn _DYNAMIC [];

-- 
I'm really pavel@ucw.cz. Look at http://195.113.31.123/~pavel.  Pavel
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!

- 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/