Re: secure levels?

Alexander Kjeldaas (astor@guardian.no)
Sun, 19 Jul 1998 14:54:58 +0200


--iEDa0DAkWCtVeE4t
Content-Type: text/plain; charset=us-ascii

On Sat, Jul 18, 1998 at 09:57:31PM -0300, Adriano Nagelschmidt Rodrigues wrote:
> Hi,
>
> I can't find a config option to turn bsd sec levels on anymore (with
> 2.1.109)... Yet I remember seeing /proc/sys/kernel/securelevel some months
> ago. But maybe I had too much beer that day? ;-)
>
> Also, does someone know if the userland tools that go with it are available
> (chflags, ls -o)?
>

Securelevels have been removed from the kernel. Instead of using
securelevels, we use POSIX-style capabilities (not real capabilities)
which is a bitmask attached to each process that sais what super-user
rights it has. The granularity of control is therefore much finer in
2.1. You don't have to remove some feature from _all_ programs, you
can remove it from _most_ programs instead. The capabilities are
defined in include/linux/capability.h. For most purposes, the new
capabilities can emulate the old securelevel by removing a certain
capability from all processes on the system.

There are certain things the new capabilities cannot emulate - for
instance, stopping access to block devices. However, I have an
experimental patch that makes it possible to emulate this side of
securelevel too by introducing a set of capabilities (CAP_USER_*) that
default to "on" for normal users. All the other capabilities
correspond to super-user privileges and therefore default to "off" for
user-level processes. The new CAP_USER_* capabilities control access
to sockets, pipes, char-, and block devices. The patch is attached if
you want to play with it (patch against 2.1.106).

There are some utilities for playing with capabilities in the libcap
library available from:

ftp://ftp.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.1/

Btw, the POSIX draft-standard that was followed when implementing this
scheme in Linux has now been officially withdrawn. Too bad, but I
guess the market wasn't ready to standardize at this point.

astor

-- 
 Alexander Kjeldaas, Guardian Networks AS, Trondheim, Norway
 http://www.guardian.no/

--iEDa0DAkWCtVeE4t Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="samsix.diff3"

diff -ur linux/fs/exec.c linux.patched/fs/exec.c --- linux/fs/exec.c Thu May 21 23:21:31 1998 +++ linux.patched/fs/exec.c Sat Jul 11 17:27:39 1998 @@ -609,9 +609,9 @@ } /* We don't have VFS support for capabilities yet */ - cap_clear(bprm->cap_inheritable); + bprm->cap_inheritable = 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 diff -ur linux/fs/open.c linux.patched/fs/open.c --- linux/fs/open.c Sat May 9 07:55:06 1998 +++ linux.patched/fs/open.c Sat Jul 11 19:13:13 1998 @@ -667,6 +667,18 @@ } f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); + if ((S_ISSOCK(inode->i_mode) && !capable(CAP_USER_SOCK)) || + (S_ISBLK(inode->i_mode) && !capable(CAP_USER_OPEN_BLOCK)) || + (S_ISCHR(inode->i_mode) && !capable(CAP_USER_OPEN_CHAR)) || + (S_ISFIFO(inode->i_mode) && !capable(CAP_USER_OPEN_FIFO))) { + error = -EPERM; + goto cleanup_all; + } + fd_install(fd, f); return 0; diff -ur linux/include/linux/capability.h linux.patched/include/linux/capability.h --- linux/include/linux/capability.h Sat Jun 13 22:48:22 1998 +++ linux.patched/include/linux/capability.h Sat Jul 11 18:46:22 1998 @@ -85,10 +85,6 @@ #define CAP_FSETID 4 -/* Used to decide between falling back on the old suser() or fsuser(). */ - -#define CAP_FS_MASK 0x1f - /* Overrides the restriction that the real or effective user ID of a process sending a signal must match the real or effective user ID of the process receiving the signal. */ @@ -254,21 +250,46 @@ #define CAP_SYS_TTY_CONFIG 26 +/* Allow opening of sockets using open() as well as through socket(), + socketpair() and socketcall(). It does _not_ prevent the process + from obtaining a socket using file-descriptor passing. */ +#define CAP_USER_SOCK 27 + +/* Allow opening of fifos using open(). This does _not_ prevent the + process from calling pipe() or obtain a fifo using file-descriptor + passing. */ +#define CAP_USER_OPEN_FIFO 28 + +/* Allow opening of block devices. This does not prevent the process + from obtaining an open file-descriptor to a block device using + file-descriptor passing. */ +#define CAP_USER_OPEN_BLOCK 29 + +/* Allow opening of character devices. Same semantics as above. */ +#define CAP_USER_OPEN_CHAR 30 + #ifdef __KERNEL__ /* * Internal kernel functions only */ +#define CAP_ROOT_SET { ~0 & ~(CAP_TO_MASK(CAP_USER_SOCK) | \ + CAP_TO_MASK(CAP_USER_OPEN_FIFO) | \ + CAP_TO_MASK(CAP_USER_OPEN_BLOCK) | \ + CAP_TO_MASK(CAP_USER_OPEN_CHAR)) } +#define CAP_FS_SET { (CAP_TO_MASK(CAP_CHOWN) | \ + CAP_TO_MASK(CAP_DAC_OVERRIDE) | \ + CAP_TO_MASK(CAP_DAC_READ_SEARCH) | \ + CAP_TO_MASK(CAP_FOWNER) | \ + CAP_TO_MASK(CAP_FSETID)) } #define CAP_EMPTY_SET { 0 } #define CAP_FULL_SET { ~0 } -#define CAP_INIT_EFF_SET { ~0 & ~CAP_TO_MASK(CAP_SETPCAP) } -#define CAP_INIT_INH_SET { ~0 & ~CAP_TO_MASK(CAP_SETPCAP) } +#define CAP_INIT_EFF_SET { ~0 } +#define CAP_INIT_INH_SET { ~0 } #define CAP_TO_MASK(x) (1 << (x)) -#define cap_raise(c, flag) ((c).cap |= CAP_TO_MASK(flag)) -#define cap_lower(c, flag) ((c).cap &= ~CAP_TO_MASK(flag)) -#define cap_raised(c, flag) ((c).cap & CAP_TO_MASK(flag)) +#define CAP_RAISED(c, flag) ((c).cap & CAP_TO_MASK(flag)) static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b) { @@ -304,8 +325,8 @@ #define cap_clear(c) do { (c).cap = 0; } while(0) #define cap_set_full(c) do { (c).cap = ~0; } while(0) #define cap_mask(c,mask) do { (c).cap &= (mask).cap; } while(0) - -#define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK) +#define cap_del(c,mask) do { (c).cap &= ~(mask).cap; } while(0) +#define cap_add(c,mask) do { (c).cap |= (mask).cap; } while(0) #endif /* __KERNEL__ */ diff -ur linux/include/linux/sched.h linux.patched/include/linux/sched.h --- linux/include/linux/sched.h Sat Jun 13 22:48:22 1998 +++ linux.patched/include/linux/sched.h Sat Jul 11 18:46:22 1998 @@ -542,6 +542,8 @@ void *dev_id); extern void free_irq(unsigned int irq, void *dev_id); +#define suser() capable(CAP_SYS_ADMIN) + /* * This has now become a routine instead of a macro, it sets a flag if * it returns true (to do BSD-style accounting where the process is flagged @@ -553,28 +555,8 @@ * fsuser(). This is done, along with moving fsuser() checks to be * last. * - * These will be removed, but in the mean time, when the SECURE_NOROOT - * flag is set, uids don't grant privilege. - */ -extern inline int suser(void) -{ - if (!issecure(SECURE_NOROOT) && current->euid == 0) { - current->flags |= PF_SUPERPRIV; - return 1; - } - return 0; -} - -extern inline int fsuser(void) -{ - if (!issecure(SECURE_NOROOT) && current->fsuid == 0) { - current->flags |= PF_SUPERPRIV; - return 1; - } - return 0; -} - -/* + * [...] + * * capable() checks for a particular capability. * New privilege checks should use this interface, rather than suser() or * fsuser(). See include/linux/capability.h for defined capabilities. @@ -583,7 +565,7 @@ extern inline int capable(int cap) { #if 1 /* ok now */ - if (cap_raised(current->cap_effective, cap)) + if (CAP_RAISED(current->cap_effective, cap)) #else if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) #endif Only in linux.patched/include/linux: sched.h~ diff -ur linux/kernel/sys.c linux.patched/kernel/sys.c --- linux/kernel/sys.c Mon May 25 21:17:26 1998 +++ linux.patched/kernel/sys.c Sat Jul 11 16:40:29 1998 @@ -324,10 +324,10 @@ * * 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of * {r,e,s}uid != 0, the permitted and effective capabilities are - * cleared. + * modified. * * 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective - * capabilities of the process are cleared. + * capabilities of the process are modified. * * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective * capabilities are set to the permitted capabilities. @@ -340,13 +340,14 @@ extern inline void cap_emulate_setxuid(int old_ruid, int old_euid, int old_suid) { + kernel_cap_t cap_root_set = CAP_ROOT_SET; if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) && (current->uid != 0 && current->euid != 0 && current->suid != 0)) { - cap_clear(current->cap_permitted); - cap_clear(current->cap_effective); + cap_del(current->cap_permitted, cap_root_set); + cap_del(current->cap_effective, cap_root_set); } if (old_euid == 0 && current->euid != 0) { - cap_clear(current->cap_effective); + cap_del(current->cap_effective, cap_root_set); } if (old_euid != 0 && current->euid == 0) { current->cap_effective = current->cap_permitted; @@ -582,17 +583,19 @@ * operate on the fs-specific bits of the process' effective * capabilities * - * FIXME - is fsuser used for all CAP_FS_MASK capabilities? + * FIXME - is fsuser used for all CAP_FS_SET capabilities? * if not, we might be a bit too harsh here. */ if (!issecure(SECURE_NO_SETUID_FIXUP)) { + kernel_cap_t cap_fs_mask = CAP_FS_SET; if (old_fsuid == 0 && current->fsuid != 0) { - current->cap_effective.cap &= ~CAP_FS_MASK; + cap_del(current->cap_effective, cap_fs_mask); } if (old_fsuid != 0 && current->fsuid == 0) { - current->cap_effective.cap |= - (current->cap_permitted.cap & CAP_FS_MASK); + cap_add(current->cap_effective, + cap_intersect(current->cap_permitted, + cap_fs_mask)); } } Only in linux.patched/kernel: sys.c~ diff -ur linux/net/socket.c linux.patched/net/socket.c --- linux/net/socket.c Sun Apr 12 02:18:16 1998 +++ linux.patched/net/socket.c Sat Jul 11 16:03:34 1998 @@ -638,6 +638,9 @@ int retval; struct socket *sock; + if (!capable(CAP_USER_SOCK)) + return -EPERM; + lock_kernel(); retval = sock_create(family, type, protocol, &sock);

--iEDa0DAkWCtVeE4t--

- 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.altern.org/andrebalsa/doc/lkml-faq.html