capabilities patch that more "pure draft"

Y2K (y2k@y2ker.com)
Sat, 1 May 1999 21:29:24 -0700 (PDT)


Moved controversial change of formula out of compute_creds and made it
optional on a per-binary basis via ECF_MAKE_SOILED flag in elf header.
SECURE_PURE_CAP now is SECURE_STRICT_FX and will only control default fI
and fE values if vfs system can't or won't provide cap information.
I think the system is very flexible and should help smooth out some of the
points of contention. I hope to merge it with the fcaps/libcap stuff soon.
Is this change acceptible to those of you who thought I deviated from the
draft too much?

This patch is off of 2.2.7 .

diff -urP linux/Documentation/Configure.help linux-cap/Documentation/Configure.help
--- linux/Documentation/Configure.help Sat May 1 14:39:55 1999
+++ linux-cap/Documentation/Configure.help Sat May 1 14:59:27 1999
@@ -1572,6 +1572,39 @@
you have use for it; the module is called binfmt_misc.o. If you
don't know what to answer at this point, say Y.

+Securebits no root !
+CONFIG_SECUREBITS_NOROOT
+ This is experimental tweak that will effect SECUREBITS_DEFAULT
+ 0 is insecure but not fixed
+ 1 is secure but not fixed
+ 2 is insecure and fixed
+ 3 is secure and fixed
+ Your userland probably doesn't understand capabilities well enough yet!
+ A secure setting will most likely cause your whole system to become unusable!
+ If you don't know what to answer at this point, say 2!
+
+Securebits no setuid fixup !
+CONFIG_SECUREBITS_NO_SETUID_FIXUP
+ This is experimental tweak that will effect SECUREBITS_DEFAULT
+ 0 is insecure but not fixed
+ 1 is secure but not fixed
+ 2 is insecure and fixed
+ 3 is secure and fixed
+ Your userland probably doesn't understand capabilities well enough yet!
+ A secure setting will most likely cause your whole system to become unusable!
+ If you don't know what to answer at this point, say 2!
+
+Securebits pure capabilites !
+CONFIG_SECUREBITS_STRICT_FX
+ This is experimental tweak that will effect SECUREBITS_DEFAULT
+ 0 is insecure but not fixed
+ 1 is secure but not fixed
+ 2 is insecure and fixed
+ 3 is secure and fixed
+ Your userland probably doesn't understand capabilities well enough yet!
+ A secure setting will most likely cause your whole system to become unusable!
+ If you don't know what to answer at this point, say 2!
+
Solaris binary emulation
CONFIG_SOLARIS_EMUL
This is experimental code which will enable you to run (many)
diff -urP linux/arch/i386/config.in linux-cap/arch/i386/config.in
--- linux/arch/i386/config.in Sat May 1 14:39:55 1999
+++ linux-cap/arch/i386/config.in Sat May 1 15:20:46 1999
@@ -112,6 +112,12 @@
bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS
fi

+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ int 'Securebits NOROOT 0,1,2,3' CONFIG_SECUREBITS_NOROOT 2
+ int 'Securebits NO_SETUID_FIXUP 0,1,2,3' CONFIG_SECUREBITS_NO_SETUID_FIXUP 2
+ int 'Securebits STRICT_FX 0,1,2,3' CONFIG_SECUREBITS_STRICT_FX 2
+fi
+
endmenu

source drivers/pnp/Config.in
diff -urP linux/cap.ldo linux-cap/cap.ldo
--- linux/cap.ldo Wed Dec 31 16:00:00 1969
+++ linux-cap/cap.ldo Sat May 1 17:41:26 1999
@@ -0,0 +1,26 @@
+/* sample fake cap ldo thingy */
+/* ld -i -T cap.lds cap.ldo */
+
+SECTIONS {
+ .caps (NOLOAD) : {
+LONG(4); /* name size */
+LONG(64); /* content size */
+LONG(7); /* content type */
+BYTE(67); /* C */
+BYTE(65); /* A */
+BYTE(80); /* P */
+BYTE(83); /* S */
+/* CAPS is name of note */
+LONG(0xca5ab1e); /* yet another signature */
+LONG(0x19990501); /* version */
+LONG(4); /* flags */
+LONG(1); /* xuid */
+LONG(-1); /* effective fE */
+LONG(0); /* permitted fP */
+LONG(-1); /* inheritable fI */
+LONG(0); /* required fR */
+LONG(0); /* known fK -- another source of version info? */
+LONG(0); /* disinherite fD aka fM */
+LONG(0); /* padding */
+ } :pcaps
+}
diff -urP linux/cap.lds linux-cap/cap.lds
--- linux/cap.lds Wed Dec 31 16:00:00 1969
+++ linux-cap/cap.lds Sat May 1 14:58:54 1999
@@ -0,0 +1,10 @@
+/* cap.lds cap ld script */
+/* ld -i -T cap.lds foo.ldo */
+
+OUTPUT(cap.o)
+PHDRS {
+pcaps PT_NOTE /* PHDRS */ ;
+}
+SECTIONS {
+ .caps (NOLOAD) : { *(.caps) } : pcaps
+}
diff -urP linux/fs/attr.c linux-cap/fs/attr.c
--- linux/fs/attr.c Fri Nov 13 10:07:26 1998
+++ linux-cap/fs/attr.c Sat May 1 14:58:54 1999
@@ -37,6 +37,9 @@
if (ia_valid & ATTR_MODE) {
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
goto error;
+ /* Also check the setuid bit! */
+ if ( (inode->i_uid==0) && !capable(CAP_SETFCAP) )
+ attr->ia_mode &= ~S_ISUID;
/* Also check the setgid bit! */
if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
inode->i_gid) && !capable(CAP_FSETID))
@@ -74,6 +77,8 @@
inode->i_mode = attr->ia_mode;
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
inode->i_mode &= ~S_ISGID;
+ if ( (inode->i_uid==0) && !capable(CAP_SETFCAP) )
+ attr->ia_mode &= ~S_ISUID;
}
mark_inode_dirty(inode);
}
diff -urP linux/fs/binfmt_elf.c linux-cap/fs/binfmt_elf.c
--- linux/fs/binfmt_elf.c Wed Apr 28 17:39:29 1999
+++ linux-cap/fs/binfmt_elf.c Sat May 1 19:54:01 1999
@@ -396,6 +396,69 @@
#define INTERPRETER_AOUT 1
#define INTERPRETER_ELF 2

+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/* Given a pointer to a set of notes, find the first with the given
+ name and type */
+static void *find_note(char *buf, int bufsz, const char *notename, int type)
+{
+ while(bufsz > sizeof(Elf32_Nhdr)) {
+ Elf32_Nhdr *nhdr = (Elf32_Nhdr *)buf;
+ char *name;
+ void *desc;
+ int sz;
+
+ sz = sizeof(Elf32_Nhdr) +
+ roundup(nhdr->n_namesz, 4) +
+ roundup(nhdr->n_descsz, 4);
+ if (bufsz < sz)
+ break;
+ name = buf + sizeof(Elf32_Nhdr);
+ desc = name + roundup(nhdr->n_namesz, 4);
+
+ if (strncmp(name, notename, nhdr->n_namesz) == 0 &&
+ type == nhdr->n_type)
+ return desc;
+
+ buf += sz;
+ bufsz -= sz;
+ }
+
+ return NULL;
+}
+
+/* restrict capabilities if we find a "CAPS" note */
+static inline int
+do_elfcap_restrict(struct linux_binprm *bprm,struct elf_capabilities_note *note)
+{
+ /* check version */
+ if (note->cap.signature!=0xca5ab1e || note->cap.version!=0x19990501)
+ return -ENOEXEC;
+ /* You may want to loose owner's uid */
+ if (note->cap.flags & ECF_MAKE_EUID_UID)
+ bprm->e_uid = current->uid;
+ /* We only honour random uid changes for root */
+ /* we also care about CAP_SETUID here */
+ if (!bprm->e_uid&&(note->cap.flags&ECF_MAKE_EUID_XUID) ) {
+ if (capable(CAP_SETUID)||cap_raised(bprm->cap_permitted,CAP_SETUID))
+ bprm->e_uid = note->cap.xuid;
+ else
+ return -EPERM;
+ }
+ /* standard restrictions */
+ cap_mask( bprm->cap_effective, note->cap.effective );
+ cap_mask( bprm->cap_permitted, note->cap.permitted );
+ cap_mask( bprm->cap_inheritable, note->cap.inheritable );
+ /* restrict even more if soiled */
+ if (note->cap.flags & ECF_MAKE_SOILED )
+ cap_mask( bprm->cap_inheritable, current->cap_permitted );
+ /* make sure we have everything required */
+ if (!cap_issubset(note->cap.required,bprm->cap_inheritable) )
+ return -EPERM;
+ /* set things up so we can disinherite at compute_creds */
+ bprm->cap_disinherite=note->cap.disinherite;
+ return 0;
+}

static inline int
do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
@@ -425,8 +488,10 @@

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;
+ if (strncmp(&elf_ex.e_ident[1], "ELF", 3) &&
+ strncmp(&elf_ex.e_ident[1], "FLE", 3))
goto out;

if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
@@ -473,7 +538,52 @@
end_data = 0;

for (i = 0; i < elf_ex.e_phnum; i++) {
- if (elf_ppnt->p_type == PT_INTERP) {
+ switch(elf_ppnt->p_type) {
+ case PT_NOTE: {
+ struct elf_capabilities_note *caps;
+ char *buf;
+ int subret = 0;
+
+ retval = -ENOMEM;
+ buf = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
+
+ if (buf == NULL)
+ goto out_free_dentry;
+
+ retval = read_exec(bprm->dentry, elf_ppnt->p_offset,
+ buf, elf_ppnt->p_filesz, 1);
+
+ if (retval < 0) {
+ kfree(buf);
+ goto out_free_dentry;
+ }
+
+ caps = (struct elf_capabilities_note *)find_note(buf,
+ elf_ppnt->p_filesz, "CAPS", NT_ELF_CAP);
+
+ if (caps != NULL) {
+ /* what to do with multiple CAPS notes?
+ Find the most restrictive union? */
+
+ /* process caps here */
+ subret=do_elfcap_restrict(bprm, caps);
+ if (subret==-ENOEXEC) {
+ printk("found bad CAPS note!\n");
+ subret=0;
+ }
+ else
+ printk("found CAPS note!\n");
+ }
+
+ kfree(buf);
+ if (subret) {
+ retval=subret;
+ goto out_free_ph;
+ }
+ }
+ break;
+
+ case PT_INTERP: {
retval = -EINVAL;
if (elf_interpreter)
goto out_free_interp;
@@ -533,6 +643,8 @@
interp_ex = *((struct exec *) bprm->buf);
interp_elf_ex = *((struct elfhdr *) bprm->buf);
}
+ break;
+ }
elf_ppnt++;
}

@@ -777,7 +889,8 @@

/* error cleanup */
out_free_dentry:
- dput(interpreter_dentry);
+ if (interpreter_dentry)
+ dput(interpreter_dentry);
out_free_interp:
if (elf_interpreter)
kfree(elf_interpreter);
diff -urP 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 Sat May 1 18:54:49 1999
@@ -601,29 +601,57 @@
id_change = 1;
}

- /* We don't have VFS support for capabilities yet */
- cap_clear(bprm->cap_inheritable);
- cap_clear(bprm->cap_permitted);
- cap_clear(bprm->cap_effective);
+ cap_clear(bprm->cap_disinherite);
+ /* This patch doesn't have VFS support for capabilities yet */
+ /* It might look something like this when it does */
+ /*
+ * int subret;
+ * subret=vfs_cap_dentry(dentry,CAP_FS_GET,
+ * &bprm->cap_effective,
+ * &bprm->cap_inheritable,
+ * &bprm->cap_permitted);
+ * if (subret==-ENOSYS||subret==-ENODATA)
+ * USE_DEFAULTS_AS_BELOW
+ * else if (subret)
+ * return subret;
+ * else cap_mask(bprm->cap_inheritable,current->cap_inheritable);
+ */
+ if (issecure(SECURE_STRICT_FX) ) {
+ cap_clear(bprm->cap_inheritable);
+ cap_clear(bprm->cap_permitted);
+ cap_clear(bprm->cap_effective);
+ }
+ else {
+ /* these are some useful defaults */
+ cap_t(bprm->cap_inheritable)=cap_t(current->cap_permitted);
+ cap_mask(bprm->cap_inheritable,current->cap_inheritable);
+ cap_clear(bprm->cap_permitted);
+ cap_set_full(bprm->cap_effective);
+ }

+ /* FIXME: Better explanation needed here, especially after changes */
/* 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)
+ cap_clear(bprm->cap_effective);
+ }
+ else if (!issecure(SECURE_NO_SETUID_FIXUP)&&
+ current->uid==0&&current->euid) {
+ cap_clear(bprm->cap_effective);
}

/* Only if pP' is _not_ a subset of pP, do we consider there
@@ -688,6 +716,7 @@
cap_t(current->cap_permitted) = new_permitted;
cap_t(current->cap_effective) = new_permitted &
cap_t(bprm->cap_effective);
+ cap_t(current->cap_inheritable) &= ~(cap_t(bprm->cap_disinherite));
}

/* AUD: Audit candidate if current->cap_effective is set */
diff -urP linux/fs/open.c linux-cap/fs/open.c
--- linux/fs/open.c Wed Apr 28 17:40:14 1999
+++ linux-cap/fs/open.c Sat May 1 14:58:54 1999
@@ -296,7 +296,7 @@
current->fsgid = current->gid;

/* Clear the capabilities if we switch to a non-root user */
- if (current->uid)
+ if (current->uid||issecure(SECURE_NOROOT))
cap_clear(current->cap_effective);
else
current->cap_effective = current->cap_permitted;
diff -urP linux/include/linux/binfmts.h linux-cap/include/linux/binfmts.h
--- linux/include/linux/binfmts.h Mon Feb 22 14:50:25 1999
+++ linux-cap/include/linux/binfmts.h Sat May 1 14:58:55 1999
@@ -25,6 +25,7 @@
struct dentry * dentry;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
+ kernel_cap_t cap_disinherite;
int argc, envc;
char * filename; /* Name of binary */
unsigned long loader, exec;
diff -urP linux/include/linux/capability.h linux-cap/include/linux/capability.h
--- linux/include/linux/capability.h Mon Feb 22 14:50:24 1999
+++ linux-cap/include/linux/capability.h Sat May 1 17:06:43 1999
@@ -264,6 +264,28 @@

#define CAP_SYS_TTY_CONFIG 26

+/* Allow changes to capability flags associated with files */
+
+#define CAP_SETFCAP 27
+
+/* These following caps can not really exist right now */
+/* for discussion only */
+/* they are greater than 31 */
+
+/* Allow changing of securebits stuff */
+
+#define CAP_LINUX_SECUREBITS 34
+
+/* linux will ignore these. used by user-land only */
+
+#define CAP_LINUX_USR_0 96
+#define CAP_LINUX_USR_15 111
+
+/* reserved for new caps that may be made that normaly everyone has */
+#define CAP_LINUX_RESERVE_NORMAL_0 112
+#define CAP_LINUX_RESERVE_NORMAL_15 127
+
+
#ifdef __KERNEL__

/*
diff -urP 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 Sat May 1 15:45:42 1999
@@ -476,6 +476,7 @@
#define NT_PRFPREG 2
#define NT_PRPSINFO 3
#define NT_TASKSTRUCT 4
+#define NT_ELF_CAP 7

/* Note header in a PT_NOTE section */
typedef struct elf32_note {
@@ -495,6 +496,32 @@
Elf32_Word n_descsz; /* Content size */
Elf32_Word n_type; /* Content type */
} Elf64_Nhdr;
+
+/* Capabilities support
+ */
+struct elf_capabilities {
+ Elf32_Word signature; /* 0xca5ab1e */
+ Elf32_Word version;
+/* Currently 0x19990501, 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
+#define ECF_MAKE_SOILED 4
+ Elf32_Word xuid;
+ Elf32_Word effective; /* fE */
+ Elf32_Word permitted; /* fP */
+ Elf32_Word inheritable; /* fI */
+ Elf32_Word required; /* fR */
+ Elf32_Word known; /* fK */
+ Elf32_Word disinherite; /* fD aka fM */
+ Elf32_Word pad; /* extra padding, pretty thin right now */
+};
+
+struct elf_capabilities_note {
+ Elf32_Nhdr notehdr;
+ Elf32_Word note_signature; /* CAPS 0x43415053 */
+ struct elf_capabilities cap;
+};

#if ELF_CLASS == ELFCLASS32

diff -urP 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 Sat May 1 16:38:34 1999
@@ -1,7 +1,22 @@
#ifndef _LINUX_SECUREBITS_H
#define _LINUX_SECUREBITS_H 1

-#define SECUREBITS_DEFAULT 0x00000000
+#ifndef CONFIG_SECUREBITS_NOROOT
+#define CONFIG_SECUREBITS_NOROOT 0x00000002
+#endif
+
+#ifndef CONFIG_SECUREBITS_NO_SETUID_FIXUP
+#define CONFIG_SECUREBITS_NO_SETUID_FIXUP 0x00000002
+#endif
+
+#ifndef CONFIG_SECUREBITS_STRICT_FX
+#define CONFIG_SECUREBITS_STRICT_FX 0x00000002
+#endif
+
+#define SECUREBITS_DEFAULT ( CONFIG_SECUREBITS_NOROOT | \
+ (CONFIG_SECUREBITS_NO_SETUID_FIXUP << 2) | \
+ (CONFIG_SECUREBITS_STRICT_FX << 4) )
+

extern unsigned securebits;

@@ -11,12 +26,22 @@
*of the executable file* if the effective uid of the new process is
0. If the real uid is 0, we raise the inheritable bitmask of the
executable file. */
+/* FIXME: the above description isn't quite accurate anymore */
+/* When set UID 0 has no special priviledges. When unset, we support
+ setuid root raising permitted bitmask upon exec,
+ and sys_access not clearing caps temporarily,
+ and fsuser and suser work differently,
+ and other stuff maybe */
#define SECURE_NOROOT 0

/* When set, setuid to/from uid 0 does not trigger capability-"fixes"
to be compatible with old programs relying on set*uid to loose
privileges. When unset, setuid doesn't change privileges. */
#define SECURE_NO_SETUID_FIXUP 2
+
+/* When set, fI and fE are set to zero when vfs info is not availible */
+/* When unset fI=pP&pI and fE=~0 when vfs info is not availible */
+#define SECURE_STRICT_FX 4

/* Each securesetting is implemented using two bits. One bit specify
whether the setting is on or off. The other bit specify whether the
diff -urP 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 Sat May 1 14:58:55 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,8 @@
}
if (old_euid != 0 && current->euid == 0) {
current->cap_effective = current->cap_permitted;
+ current->cap_inheritable=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/