Latest capabilities patch(getting close)

Y2K (y2k@y2ker.com)
Mon, 26 Apr 1999 11:16:13 -0700 (PDT)


Supports "soiled" and "pure draft".
Preliminary support for required caps in elf header.
per task or global securebits settings.
Elf note fixups.
ld scripts that might useful, might not.
I hope that this is close enough that some real testing might begin soon.
Once elf cap adder is finished then you can play with caps RSN.

diff -urP 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 Thu Apr 22 22:07:37 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_PURE_CAP
+ 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 Mon Feb 1 12:03:20 1999
+++ linux-cap/arch/i386/config.in Thu Apr 22 22:08:40 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 PURE_CAP 0,1,2,3' CONFIG_SECUREBITS_PURE_CAP 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 Mon Apr 26 10:10:37 1999
@@ -0,0 +1,27 @@
+/* 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(0); /* version */
+LONG(0); /* flags */
+LONG(1); /* xuid */
+LONG(0); /* securebits */
+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 Mon Apr 26 08:19:29 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 Wed Apr 21 20:10:36 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_SETPCAP) )
+ 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_SETPCAP) )
+ 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 21 00:15:27 1999
+++ linux-cap/fs/binfmt_elf.c Mon Apr 26 10:16:18 1999
@@ -425,8 +425,11 @@

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,6 +476,59 @@
end_data = 0;

for (i = 0; i < elf_ex.e_phnum; i++) {
+ 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;
+ if (note.notehdr.n_type!=NT_ELF_CAP)
+ 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 || note.cap.version) {
+ printk( "signature = %x, version = %x, header @ %x\n", note.cap.signature, note.cap.version, elf_ppnt->p_offset );
+ goto out_free_ph;
+ }
+ if (!cap_issubset(note.cap.required,bprm->cap_inheritable) ) {
+ retval=-EPERM;
+ 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)||
+ cap_raised(bprm->permitted,CAP_SETUID)) ) {
+ /* We only honour random uid changes for root */
+ /* maybe we should 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 );
+ if (note.cap.securebits&&capable(CAP_LINUX_SECUREBITS) )
+ SECUREBITS_SET(bprm->securebits,
+ note.cap.securebits);
+ else if (note.cap.securebits) {
+ retval=-EPERM;
+ goto out_free_ph;
+ }
+
+ printk( "Now: uid = %d, effective = %x, permitted = %x, inheritable = %x\n", bprm->e_uid, bprm->cap_effective, bprm->cap_permitted, bprm->cap_inheritable );
+
+ }
if (elf_ppnt->p_type == PT_INTERP) {
retval = -EINVAL;
if (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 Sun Apr 25 19:41:04 1999
@@ -580,6 +580,7 @@

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

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

/* We don't have VFS support for capabilities yet */
- cap_clear(bprm->cap_inheritable);
- cap_clear(bprm->cap_permitted);
- cap_clear(bprm->cap_effective);
+ /* if we did we'd do something like this pseudo */
+ /* if (HAD_VFS_CAPS_AVAIL) {
+ * USE_VFS_CAPS
+ * else {
+ * USE_DEFAULTS_AS_BELOW
+ * }
+ */
+ if (issecure(SECURE_PURE_CAP) ) {
+ 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_clear(bprm->cap_permitted);
+ 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)
+ /* HELP: FIXME: should the below be SECURE_NO_SETUID_FIXUP ? */
+ /* if (!issecure(SECURE_NO_SETUID_FIXUP)&&(mode&S_ISUID) ) { */
+ 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 +687,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
@@ -677,9 +697,13 @@

void compute_creds(struct linux_binprm *bprm)
{
- int new_permitted = cap_t(bprm->cap_permitted) |
- (cap_t(bprm->cap_inheritable) &
- cap_t(current->cap_inheritable));
+ int new_permitted = cap_t(bprm->cap_inheritable) &
+ cap_t(current->cap_inheritable);
+
+ if (!issecure(SECURE_CAP_PURE))
+ cap_intersect(new_permitted,current->cap_permitted);
+
+ cap_combine(new_permitted,cap_t(bprm->cap_permitted) );

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

current->suid = current->euid = current->fsuid = bprm->e_uid;
current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->securebits=bprm->securebits | securebits;
+ /* HELP: FIXME: should the below be SECURE_NO_SETUID_FIXUP ? */
+ /* if (issecure(SECURE_NO_SETUID_FIXUP)||current->euid) */
+ if (!issecure(SECURE_CAP_PURE)&&
+ (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 -urP linux/fs/open.c linux-cap/fs/open.c
--- linux/fs/open.c Wed Apr 21 00:16:09 1999
+++ linux-cap/fs/open.c Wed Apr 21 19:56:36 1999
@@ -295,11 +295,8 @@
current->fsuid = current->uid;
current->fsgid = current->gid;

- /* Clear the capabilities if we switch to a non-root user */
- if (current->uid)
- cap_clear(current->cap_effective);
- else
- current->cap_effective = current->cap_permitted;
+ /* Clear the capabilities */
+ cap_clear(current->cap_effective);

dentry = namei(filename);
res = PTR_ERR(dentry);
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 Thu Apr 22 21:56:55 1999
@@ -264,6 +264,24 @@

#define CAP_SYS_TTY_CONFIG 26

+/* Allow changing of securebits stuff */
+
+#define CAP_LINUX_SECUREBITS 27
+
+/* These following caps can not really exist right now */
+/* for discussion only */
+/* they are greater than 31 */
+
+/* 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 Mon Apr 26 10:21:21 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,33 @@
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 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;
+ /* we can have per process issecure(SECURE_foo) */
+ 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/sched.h linux-cap/include/linux/sched.h
--- linux/include/linux/sched.h Wed Apr 21 00:15:50 1999
+++ linux-cap/include/linux/sched.h Wed Apr 21 21:09:51 1999
@@ -285,6 +285,7 @@
int ngroups;
gid_t groups[NGROUPS];
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ unsigned securebits;
struct user_struct *user;
/* limits */
struct rlimit rlim[RLIM_NLIMITS];
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 Thu Apr 22 23:47:43 1999
@@ -1,7 +1,23 @@
#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_PURE_CAP
+#define CONFIG_SECUREBITS_PURE_CAP 0x00000002
+#endif
+
+#define SECUREBITS_DEFAULT ( CONFIG_SECUREBITS_NOROOT | \
+ (CONFIG_SECUREBITS_NO_SETUID_FIXUP << 2) | \
+ (CONFIG_SECUREBITS_PURE_CAP << 4) )
+
+#define SECUREBITS_PER_TASK

extern unsigned securebits;

@@ -18,13 +34,28 @@
privileges. When unset, setuid doesn't change privileges. */
#define SECURE_NO_SETUID_FIXUP 2

+/* When set, kernel can act in "pure draft" cap mode */
+#define SECURE_PURE_CAP 4
+
/* Each securesetting is implemented using two bits. One bit specify
whether the setting is on or off. The other bit specify whether the
setting is fixed or not. A setting which is fixed cannot be changed
from user-level. */

+#ifndef SECUREBITS_PER_TASK
#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 )
+#endif
+
+#define SECUREBITS_SETA(X,Y,Z) ( X = ( (1<<(Z +1))&SECUREBITS_DEFAULT ? \
+ X : X | ((1<< Z)& Y) ) )
+#define SECUREBITS_SET(X,Y) ( SECUREBITS_SETA((X),(Y),SECURE_NOROOT) , \
+ SECUREBITS_SETA((X),(Y),SECURE_NO_SETUID_FIXUP) , \
+ SECUREBITS_SETA((X),(Y),SECURE_PURE_CAP) )

#endif /* !_LINUX_SECUREBITS_H */
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 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);
}
}


--
Warning:
When I mention capabilites I mean "soiled" capabilities not "pure draft".
Any caps I mention are *derived* from a withdrawn draft posix document.

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