cap patch snapshot

Y2K (y2k@y2ker.com)
Fri, 23 Apr 1999 00:16:06 -0700 (PDT)


A quick snapshot of my cap patch.
Elf Header stuff stolen from Pavel .
Please I'd love to hear all criticism.

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

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 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 -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 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 -ur 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 -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 Thu Apr 22 23:36:44 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,51 @@ 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; + + 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 ) { + /* 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 -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 Thu Apr 22 23:27: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 @@ -681,6 +701,9 @@ (cap_t(bprm->cap_inheritable) & cap_t(current->cap_inheritable)); + if (!issecure(SECURE_CAP_PURE)) + cap_intersect(new_permitted,current->cap_permitted); + /* For init, we want to retain the capabilities set * in the init_task struct. Thus we skip the usual * capability rules */ @@ -694,6 +717,11 @@ 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_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/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 -ur 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 -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 Thu Apr 22 23:06:50 1999 @@ -496,6 +496,44 @@ 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 effective_min; + Elf32_Word effective1; + Elf32_Word effective2; + Elf32_Word effective3; + Elf32_Word permitted; + Elf32_Word permitted_min; + Elf32_Word permitted1; + Elf32_Word permitted2; + Elf32_Word permitted3; + Elf32_Word inheritable; + Elf32_Word inheritable_min; + 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/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 -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 Thu Apr 22 22:04:28 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" 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 -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/