diff options
27 files changed, 6577 insertions, 0 deletions
diff --git a/sys/contrib/lomac/kernel_interface.c b/sys/contrib/lomac/kernel_interface.c new file mode 100644 index 0000000..a825f3a --- /dev/null +++ b/sys/contrib/lomac/kernel_interface.c @@ -0,0 +1,497 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_interface.c,v 1.25 2001/10/25 21:21:59 tfraser Exp $ + */ + +#include <sys/param.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/pipe.h> +#include <sys/socketvar.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> + +#include "kernel_interface.h" +#include "lomacfs.h" +#include "kernel_log.h" + +/* + * Reuse the eflags field of proc.p_vmspace->vm_map.header (since it is + * currently not used for anything but a placeholder, and won't change + * generally...) as storage for our process-based information. + * + * This is the only really effective way to make thread-based MAC + * easy. + */ + +#define p_eflags p_vmspace->vm_map.header.eflags +#define EF_HIGHEST_LEVEL 0x00010000 +#define EF_LOWEST_LEVEL 0x00020000 +#define EF_LEVEL_MASK 0x00030000 +#define EF_ATTR_NONETDEMOTE 0x00040000 +#define EF_ATTR_NODEMOTE 0x00080000 +#define EF_ATTR_MASK 0x000c0000 + +static u_int +level2subjectbits(level_t level) { + + switch (level) { + case LOMAC_HIGHEST_LEVEL: + return (EF_HIGHEST_LEVEL); + case LOMAC_LOWEST_LEVEL: + return (EF_LOWEST_LEVEL); + default: + panic("level2subjectbits: invalid level %d\n", level); + } +} + +static level_t +subjectbits2level(u_int flags) { + + switch (flags & EF_LEVEL_MASK) { + case EF_HIGHEST_LEVEL: + /* + * During an execve(), the kernel's original execve() creates a + * new vmspace and puts it into use before it has been initialized + * by us to contain a subject level. Since this is the only case + * when a subject may have a level not set, pretend that it + * is just a high-level file, and allow the lomacfs_open() to then + * succeed. + */ + case 0: + return (LOMAC_HIGHEST_LEVEL); + case EF_LOWEST_LEVEL: + return (LOMAC_LOWEST_LEVEL); + default: + panic("subjectbits2level: invalid flags %#x\n", flags); + } +} + +static u_int +attr2subjectbits(u_int attr) { + u_int bits = 0; + + if (attr & LOMAC_ATTR_NONETDEMOTE) + bits |= EF_ATTR_NONETDEMOTE; + if (attr & LOMAC_ATTR_NODEMOTE) + bits |= EF_ATTR_NODEMOTE; + return (bits); +} + +static u_int +subjectbits2attr(u_int bits) { + u_int attr = 0; + + if (bits & EF_ATTR_NONETDEMOTE) + attr |= LOMAC_ATTR_NONETDEMOTE; + if (bits & EF_ATTR_NODEMOTE) + attr |= LOMAC_ATTR_NODEMOTE; + return (attr); +} + +static int +subject_lock(lomac_subject_t *p, int read) { +#ifdef USES_LOCKMGR + int hadlock; + + hadlock = PROC_LOCKED(p); + if (hadlock) + PROC_UNLOCK(p); +#endif + if (read) + vm_map_lock_read(&p->p_vmspace->vm_map); + else + vm_map_lock(&p->p_vmspace->vm_map); +#ifdef USES_LOCKMGR + return (hadlock); +#else + return (0); +#endif +} + +static void +subject_unlock(lomac_subject_t *p, int read, int hadlock) { + + if (read) + vm_map_unlock_read(&p->p_vmspace->vm_map); + else + vm_map_unlock(&p->p_vmspace->vm_map); +#ifdef USES_LOCKMGR + if (hadlock) + PROC_LOCK(p); +#endif +} + + +void +init_subject_lattr(lomac_subject_t *p, lattr_t *lattr) { + int s; + + s = subject_lock(p, 0); + p->p_eflags = level2subjectbits(lattr->level) | + attr2subjectbits(lattr->flags); + subject_unlock(p, 0, s); +} + +/* + * Set/get the subject level on a process. The process must not be able + * to change, so either the process must be locked on entry or it must + * be held in exclusivity otherwise (executing on behalf of via a syscall, + * including as EITHER child or parent in a fork). + */ +void +set_subject_lattr(lomac_subject_t *p, lattr_t lattr) { + int s; + +#ifdef INVARIANTS + do { + lattr_t oslattr; + + get_subject_lattr(p, &oslattr); + if (lomac_must_demote(&lattr, &oslattr)) + panic("raising subject level"); + } while (0); +#endif /* !INVARIANTS */ + s = subject_lock(p, 0); + p->p_eflags = (p->p_eflags & ~(EF_LEVEL_MASK | EF_ATTR_MASK)) | + level2subjectbits(lattr.level) | + attr2subjectbits(lattr.flags); + subject_unlock(p, 0, s); + kernel_vm_drop_perms(curthread, &lattr); +} + +void +get_subject_lattr(lomac_subject_t *p, lattr_t *lattr) { + int s; + + s = subject_lock(p, 1); + lattr->level = subjectbits2level(p->p_eflags); + lattr->flags = subjectbits2attr(p->p_eflags); + subject_unlock(p, 1, s); +} + +static __inline u_int +level2lvnodebits(level_t level) { + + switch (level) { + case LOMAC_HIGHEST_LEVEL: + return (LN_HIGHEST_LEVEL); + case LOMAC_LOWEST_LEVEL: + return (LN_LOWEST_LEVEL); + default: + panic("level2lvnodebits: invalid level %d\n", level); + } +} + +static __inline level_t +lvnodebits2level(u_int flags) { + + switch (flags & LN_LEVEL_MASK) { + case LN_HIGHEST_LEVEL: + return (LOMAC_HIGHEST_LEVEL); + case LN_LOWEST_LEVEL: + return (LOMAC_LOWEST_LEVEL); + default: + panic("lvnodebits2level: invalid flags %#x\n", flags); + } +} + +static __inline unsigned int +attr2lvnodebits(unsigned int attr) { + unsigned int bits = 0; + + if (attr & LOMAC_ATTR_LOWWRITE) + bits |= LN_ATTR_LOWWRITE; + if (attr & LOMAC_ATTR_LOWNOOPEN) + bits |= LN_ATTR_LOWNOOPEN; + if (attr & LOMAC_ATTR_NONETDEMOTE) + bits |= LN_ATTR_NONETDEMOTE; + if (attr & LOMAC_ATTR_NODEMOTE) + bits |= LN_ATTR_NODEMOTE; + return (bits); +} + +static __inline unsigned int +lvnodebits2attr(unsigned int bits) { + unsigned int attr = 0; + + if (bits & LN_ATTR_LOWWRITE) + attr |= LOMAC_ATTR_LOWWRITE; + if (bits & LN_ATTR_LOWNOOPEN) + attr |= LOMAC_ATTR_LOWNOOPEN; + if (bits & LN_ATTR_NONETDEMOTE) + attr |= LOMAC_ATTR_NONETDEMOTE; + if (bits & LN_ATTR_NODEMOTE) + attr |= LOMAC_ATTR_NODEMOTE; + return (attr); +} + +/* + * These flags correspond to the same ones set in lomac_node{}s. + */ +#define UV_LEVEL_MASK 0x08000000 +#define UV_LOWEST_LEVEL 0x00000000 +#define UV_HIGHEST_LEVEL 0x08000000 +#define UV_ATTR_LOWWRITE 0x10000000 +#define UV_ATTR_LOWNOOPEN 0x20000000 +#define UV_ATTR_NONETDEMOTE 0x40000000 +#define UV_ATTR_NODEMOTE 0x80000000 +#define UV_ATTR_MASK 0xf0000000 + +static __inline u_int +level2uvnodebits(level_t level) { + + switch (level) { + case LOMAC_HIGHEST_LEVEL: + return (UV_HIGHEST_LEVEL); + case LOMAC_LOWEST_LEVEL: + return (UV_LOWEST_LEVEL); + default: + panic("level2uvnodebits: invalid level %d\n", level); + } +} + +static __inline level_t +uvnodebits2level(u_int flags) { + + switch (flags & UV_LEVEL_MASK) { + case UV_HIGHEST_LEVEL: + return (LOMAC_HIGHEST_LEVEL); + case UV_LOWEST_LEVEL: + return (LOMAC_LOWEST_LEVEL); + default: + panic("uvnodebits2level: invalid flags %#x\n", flags); + } +} + +static __inline u_int +attr2uvnodebits(u_int attr) { + unsigned int bits = 0; + + if (attr & LOMAC_ATTR_LOWWRITE) + bits |= UV_ATTR_LOWWRITE; + if (attr & LOMAC_ATTR_LOWNOOPEN) + bits |= UV_ATTR_LOWNOOPEN; + if (attr & LOMAC_ATTR_NONETDEMOTE) + bits |= UV_ATTR_NONETDEMOTE; + if (attr & LOMAC_ATTR_NODEMOTE) + bits |= UV_ATTR_NODEMOTE; + return (bits); +} + +static __inline u_int +uvnodebits2attr(u_int bits) { + unsigned int attr = 0; + + if (bits & UV_ATTR_LOWWRITE) + attr |= LOMAC_ATTR_LOWWRITE; + if (bits & UV_ATTR_LOWNOOPEN) + attr |= LOMAC_ATTR_LOWNOOPEN; + if (bits & UV_ATTR_NONETDEMOTE) + attr |= LOMAC_ATTR_NONETDEMOTE; + if (bits & UV_ATTR_NODEMOTE) + attr |= LOMAC_ATTR_NODEMOTE; + return (attr); +} + +#define OBJ_LOWEST_LEVEL 0x8000 /* the highest level is implicit */ + +/* + * This code marks pipes with levels. We use a previously unnused bit + * in the pipe_state field of struct pipe to store the level + * information. Bit clear means LOMAC_HIGHEST_LEVEL, bit set means + * LOMAC_LOWEST_LEVEL. Since new pipes have clear bits by default, + * using clear bit as highest causes new pipes to start at the highest + * level automatically. + */ +#define PIPE_LEVEL_LOWEST 0x10000000 + +/* This code marks sockets created by socketpair() with levels. It + * uses a previouslt unused bit in the so_state field of struct socket + * to store the level information. Bit clear means + * LOMAC_HIGHEST_LEVEL, bit set means LOMAC_LOWEST_LEVEL. Since new + * sockets have clear bits by default, using clear bit as highest + * causes new sockets to start at the highest level automatically. + */ +#define SOCKET_LEVEL_LOWEST 0x4000 + +void +set_object_lattr(lomac_object_t *obj, lattr_t lattr) { + struct vnode *vp; + struct lomac_node *ln; + vm_object_t object; + struct pipe *pipe; + struct socket *socket; + + switch (obj->lo_type) { + case LO_TYPE_LVNODE: + KASSERT(VISLOMAC(obj->lo_object.vnode), + ("not a LOMACFS vnode")); + ln = VTOLOMAC(obj->lo_object.vnode); + ln->ln_flags = + (ln->ln_flags & ~(LN_LEVEL_MASK | LN_ATTR_MASK)) | + level2lvnodebits(lattr.level) | + attr2lvnodebits(lattr.flags); + break; + case LO_TYPE_UVNODE: + vp = obj->lo_object.vnode; + KASSERT(!VISLOMAC(vp), ("is a LOMACFS vnode")); + VI_LOCK(vp); + vp->v_flag = (vp->v_flag & ~(UV_LEVEL_MASK | UV_ATTR_MASK)) | + level2uvnodebits(lattr.level) | + attr2uvnodebits(lattr.flags); + VI_UNLOCK(vp); + break; + case LO_TYPE_VM_OBJECT: + object = obj->lo_object.vm_object; + KASSERT(object->type != OBJT_VNODE, ("object has a vnode")); + KASSERT(object->backing_object == NULL, + ("is a backing object")); + if (lattr.level == LOMAC_HIGHEST_LEVEL) + vm_object_clear_flag(object, OBJ_LOWEST_LEVEL); + else + vm_object_set_flag(object, OBJ_LOWEST_LEVEL); + KASSERT(lattr.flags == 0, + ("cannot set attr on a vm_object")); + break; + case LO_TYPE_PIPE: + pipe = obj->lo_object.pipe; + KASSERT(pipe->pipe_peer == NULL || + (pipe->pipe_state & PIPE_LEVEL_LOWEST) == + (pipe->pipe_peer->pipe_state & PIPE_LEVEL_LOWEST), + ("pipe attrs unsynchronized")); + if (lattr.level == LOMAC_HIGHEST_LEVEL) + pipe->pipe_state &= ~PIPE_LEVEL_LOWEST; + else + pipe->pipe_state |= PIPE_LEVEL_LOWEST; + pipe = pipe->pipe_peer; + if (pipe != NULL) { + if (lattr.level == LOMAC_HIGHEST_LEVEL) + pipe->pipe_state &= ~PIPE_LEVEL_LOWEST; + else + pipe->pipe_state |= PIPE_LEVEL_LOWEST; + } + KASSERT(lattr.flags == 0, ("cannot set attr on a pipe")); + break; + case LO_TYPE_SOCKETPAIR: + socket = obj->lo_object.socket; + /* KASSERT that socket peer levels are synchronized */ + if (lattr.level == LOMAC_HIGHEST_LEVEL) + socket->so_state &= ~SOCKET_LEVEL_LOWEST; + else + socket->so_state |= SOCKET_LEVEL_LOWEST; +#ifdef NOT_YET + pipe = pipe->pipe_peer; + if (pipe != NULL) { + if (lattr.level == LOMAC_HIGHEST_LEVEL) + pipe->pipe_state &= ~PIPE_LEVEL_LOWEST; + else + pipe->pipe_state |= PIPE_LEVEL_LOWEST; + } + KASSERT(lattr.flags == 0, ("cannot set attr on a pipe")); +#endif + break; + default: + panic("set_object_lattr: invalid lo_type %d", obj->lo_type); + } +} + +void +get_object_lattr(const lomac_object_t *obj, lattr_t *lattr) { + struct vnode *vp; + struct lomac_node *ln; + vm_object_t object; + struct pipe *pipe; + struct socket *socket; + + switch (obj->lo_type) { + case LO_TYPE_LVNODE: + KASSERT(VISLOMAC(obj->lo_object.vnode), + ("not a LOMACFS vnode")); + ln = VTOLOMAC(obj->lo_object.vnode); + lattr->level = lvnodebits2level(ln->ln_flags); + lattr->flags = lvnodebits2attr(ln->ln_flags); + break; + case LO_TYPE_UVNODE: + vp = obj->lo_object.vnode; + KASSERT(!VISLOMAC(vp), ("is a LOMACFS vnode")); + VI_LOCK(vp); + lattr->level = uvnodebits2level(vp->v_flag); + lattr->flags = uvnodebits2attr(vp->v_flag); + VI_UNLOCK(vp); + break; + case LO_TYPE_VM_OBJECT: + object = obj->lo_object.vm_object; + KASSERT(object->type != OBJT_VNODE, ("object has a vnode")); + KASSERT(object->backing_object == NULL, + ("is a backing object")); + lattr->level = (object->flags & OBJ_LOWEST_LEVEL) ? + LOMAC_LOWEST_LEVEL : LOMAC_HIGHEST_LEVEL; + lattr->flags = 0; + break; + case LO_TYPE_PIPE: + pipe = obj->lo_object.pipe; + lattr->level = (pipe->pipe_state & PIPE_LEVEL_LOWEST) ? + LOMAC_LOWEST_LEVEL : LOMAC_HIGHEST_LEVEL; + lattr->flags = 0; + break; + case LO_TYPE_SOCKETPAIR: + socket = obj->lo_object.socket; + lattr->level = (socket->so_state & SOCKET_LEVEL_LOWEST) ? + LOMAC_LOWEST_LEVEL : LOMAC_HIGHEST_LEVEL; + lattr->flags = 0; + break; + default: + panic("get_object_level: invalid lo_type %d", obj->lo_type); + } +} + +/* + * Flag certain procs, like init(8) and kthreads, as "invincible". + */ +int +subject_do_not_demote(lomac_subject_t *subj) { + int inv = 0; + + if (subj->p_pid == 1) { + inv = 1; + } else { + int had_lock = PROC_LOCKED(subj); + + if (!had_lock) + PROC_LOCK(subj); + if (subj->p_flag & P_SYSTEM) + inv = 1; + if (!had_lock) + PROC_UNLOCK(subj); + } + return (inv); +} diff --git a/sys/contrib/lomac/kernel_interface.h b/sys/contrib/lomac/kernel_interface.h new file mode 100644 index 0000000..751bc59 --- /dev/null +++ b/sys/contrib/lomac/kernel_interface.h @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_interface.h,v 1.21 2001/10/15 17:58:32 bfeldman Exp $ + */ +#ifndef KERNEL_INTERFACE_H +#define KERNEL_INTERFACE_H + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> + +#include "lomac.h" + + +/* + * We do not yet implement any categories. We use lattr_t's flags + * field to implement the "LOMAC_ATTR_LOWWRITE" exception. When + * this bit is set on a file, lowest-level processes can write to + * the file, regardless of the file's level. + * + * There is also the LOMAC_ATTR_LOWNOOPEN flag, which prevents the + * opening of a given object to subjects with a lower level than its + * own. + * + * LOMAC_ATTR_NONETDEMOTE is set on subjects to prevent demotion on + * network reads; LOMAC_ATTR_NODEMOTE is set on subjects to prevent + * all demotion. Both of these will effectively set a high-level + * subject as very trusted (and must be used sparingly). + */ +#define LOMAC_ATTR_LOWWRITE 0x00010000 +#define LOMAC_ATTR_LOWNOOPEN 0x00020000 +#define LOMAC_ATTR_NONETDEMOTE 0x00040000 +#define LOMAC_ATTR_NODEMOTE 0x00080000 + +typedef struct proc lomac_subject_t; +typedef struct lomac_object { + enum lomac_object_type { + LO_TYPE_LVNODE, /* LOMAC vnode */ + LO_TYPE_UVNODE, /* underlying vnode */ + LO_TYPE_VM_OBJECT, /* VM object, not OBJT_VNODE */ + LO_TYPE_PIPE, /* pipe */ + LO_TYPE_SOCKETPAIR /* local-domain socket in socketpair */ + } lo_type; + union { + struct vnode *vnode; + vm_object_t vm_object; + struct pipe *pipe; + struct socket *socket; + } lo_object; +} lomac_object_t; +typedef struct sbuf lomac_log_t; + +void init_subject_lattr(lomac_subject_t *, lattr_t *); +void set_subject_lattr(lomac_subject_t *, lattr_t); +void get_subject_lattr(lomac_subject_t *, lattr_t *); +void set_object_lattr(lomac_object_t *, lattr_t); +void get_object_lattr(const lomac_object_t *, lattr_t *); +int subject_do_not_demote(lomac_subject_t *); +#if 0 +void subject_propogate_lattr(lomac_subject_t *); +#endif +void kernel_vm_drop_perms(struct thread *, lattr_t *); + +#endif /* KERNEL_INTERFACE_H */ diff --git a/sys/contrib/lomac/kernel_lkm.c b/sys/contrib/lomac/kernel_lkm.c new file mode 100644 index 0000000..15addcd --- /dev/null +++ b/sys/contrib/lomac/kernel_lkm.c @@ -0,0 +1,279 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_lkm.c,v 1.26 2001/11/14 16:30:17 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/linker.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/namei.h> +#include <sys/proc.h> +#include <sys/systm.h> + +#include "kernel_interface.h" +#include "kernel_mediate.h" +#include "kernel_plm.h" +#include "kernel_util.h" +#include "kernel_pipe.h" +#include "kernel_socket.h" +#include "lomacfs.h" +#include "lomacio.h" + +static d_ioctl_t lomac_ioctl; + +#define CDEV_MAJOR 207 +#define LOMAC_MINOR 0 + +static struct cdevsw lomac_cdevsw = { + /* open */ (d_open_t *)nullop, + /* close */ (d_open_t *)nullop, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ lomac_ioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "lomac", + /* maj */ CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, +}; + +static dev_t lomac_dev = NULL; + +int +lomac_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { + struct nameidata nd; + struct proc *p; + struct proc *targp; + struct lomac_fioctl *fio; + lomac_object_t lobj; + lattr_t lattr; + int error; + + p = td->td_proc; + switch (cmd) { + case LIOGETPLEVEL: + targp = pfind(*(int *)data); + if (targp == NULL) + return (ESRCH); + if (p_cansee(p, targp) != 0) { + PROC_UNLOCK(targp); + return (ESRCH); + } + get_subject_lattr(targp, &lattr); + *(level_t *)data = lattr.level; + PROC_UNLOCK(targp); + return (0); + case LIOGETFLEVEL: + fio = (struct lomac_fioctl *)data; + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_SYSSPACE, + fio->path, td); + if ((error = namei(&nd)) != 0) + return (error); + if (VISLOMAC(nd.ni_vp)) + lobj.lo_type = LO_TYPE_LVNODE; + else + lobj.lo_type = LO_TYPE_UVNODE; + lobj.lo_object.vnode = nd.ni_vp; + get_object_lattr(&lobj, &lattr); + *(level_t *)&fio->level = lattr.level; + NDFREE(&nd, NDF_ONLY_PNBUF); + vput(nd.ni_vp); + return (error); + case LIOGETFLATTR: + fio = (struct lomac_fioctl *)data; + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_SYSSPACE, + fio->path, td); + if ((error = namei(&nd)) != 0) + return (error); + if (VISLOMAC(nd.ni_vp)) + lobj.lo_type = LO_TYPE_LVNODE; + else + lobj.lo_type = LO_TYPE_UVNODE; + lobj.lo_object.vnode = nd.ni_vp; + get_object_lattr(&lobj, (lattr_t *)&fio->level); + NDFREE(&nd, NDF_ONLY_PNBUF); + vput(nd.ni_vp); + return (error); + case LIOPMAKELOWLEVEL: + lattr.level = LOMAC_LOWEST_LEVEL; + lattr.flags = 0; + set_subject_lattr(p, lattr); + return (0); + default: + return (ENOTTY); + } +} + +int (*old_execve)(struct proc *, void *); +#ifdef __i386__ +int (*old_sysarch)(struct proc *, void *); +#endif + +/* + * This is "borrowed" from kern_module.c and MUST be kept in synch! + */ +struct module { + TAILQ_ENTRY(module) link; /* chain together all modules */ + TAILQ_ENTRY(module) flink; /* all modules in a file */ + struct linker_file* file; /* file which contains this module */ + int refs; /* reference count */ + int id; /* unique id number */ + char *name; /* module name */ + modeventhand_t handler; /* event handler */ + void *arg; /* argument for handler */ + modspecific_t data; /* module specific data */ +}; + +static int +lomac_modevent(module_t module, int event, void *unused) { + static int initialized_procs = 0; + static int initialized_syscalls = 0; + static int initialized_pipes = 0; + static int initialized_sockets = 0; + static int initialized_vm = 0; + static linker_file_t kernlf; + int error; + + switch ((enum modeventtype)event) { + case MOD_LOAD: + if (!lomac_plm_initialized) + return (EINVAL); + kernlf = linker_kernel_file; + old_execve = + (int (*)(struct proc *, void *))linker_file_lookup_symbol( + kernlf, "execve", 1); + if (old_execve == NULL) + return (ENOENT); +#ifdef __i386__ + old_sysarch = + (int (*)(struct proc *, void *))linker_file_lookup_symbol( + kernlf, "sysarch", 1); + if (old_sysarch == NULL) + return (ENOENT); +#endif + error = lomac_initialize_procs(); + if (error) + break; + initialized_procs = 1; + error = lomac_initialize_syscalls(); + if (error) + break; + initialized_syscalls = 1; + error = lomac_initialize_pipes(); + if (error) + break; + initialized_pipes = 1; + error = lomac_initialize_sockets(); + if (error) + break; + initialized_sockets = 1; + lomac_dev = make_dev(&lomac_cdevsw, LOMAC_MINOR, UID_ROOT, + GID_WHEEL, 0666, "lomac"); + linker_kernel_file = module->file; + error = vfs_mount(curthread, "lomacfs", "/", 0, NULL); + if (error) + return (error); + error = lomac_initialize_cwds(); + if (error) + return (error); + printf("LOMAC: Low-Watermark Mandatory Access Control v2.0.0\n"); + return (error); + case MOD_UNLOAD: + /* + * It's always a bad idea to let a low-security process + * unload the module providing security. + */ + if (initialized_procs && + !mediate_subject_at_level("kldunload", curthread->td_proc, + LOMAC_HIGHEST_LEVEL)) + return (EPERM); + /* + * Unloading doesn't work well at the moment... + */ + return (EPERM); + if (initialized_sockets) { + error = lomac_uninitialize_sockets(); + if (error) + break; + initialized_sockets = 0; + } + if (initialized_pipes) { + error = lomac_uninitialize_pipes(); + if (error) + break; + initialized_pipes = 0; + } + if (initialized_syscalls) { + error = lomac_uninitialize_syscalls(); + if (error) + break; + initialized_syscalls = 0; + } + if (initialized_procs) { + error = lomac_uninitialize_procs(); + if (error) + break; + initialized_procs = 0; + } + if (initialized_vm) { + error = lomac_uninitialize_vm(); + if (error) + break; + initialized_vm = 0; + } + if (lomac_dev) { + if (count_dev(lomac_dev) != 0) + return (EBUSY); + destroy_dev(lomac_dev); + } + printf("LOMAC: unloading\n"); + linker_kernel_file = kernlf; + break; + case MOD_SHUTDOWN: + break; + } + return (0); +} + +static moduledata_t lomac_moduledata = { + "lomac", + &lomac_modevent, + NULL +}; +DECLARE_MODULE(lomac, lomac_moduledata, SI_SUB_VFS, SI_ORDER_ANY); +MODULE_VERSION(lomac, 1); +MODULE_DEPEND(lomac, syscall_gate, 1, 1, 1); +MODULE_DEPEND(lomac, lomacfs, 1, 1, 1); +MODULE_DEPEND(lomac, lomac_plm, 1, 1, 1); diff --git a/sys/contrib/lomac/kernel_log.c b/sys/contrib/lomac/kernel_log.c new file mode 100644 index 0000000..59a7d7c --- /dev/null +++ b/sys/contrib/lomac/kernel_log.c @@ -0,0 +1,190 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_log.c,v 1.12 2001/10/17 15:19:26 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> + +#include "lomacfs.h" + +#include "kernel_interface.h" + +/* The following definition sets the global log verbosity level. */ +SYSCTL_NODE(_kern, OID_AUTO, lomac, CTLFLAG_RW, 0, "LOMAC"); +SYSCTL_NODE(_kern_lomac, OID_AUTO, verbose, CTLFLAG_RW, 0, "LOMAC verbosity"); +#define VERBOSITY_SETTING(level) \ + unsigned int lomac_verbose_##level## = 1; \ + SYSCTL_UINT(_kern_lomac_verbose, OID_AUTO, level, \ + CTLFLAG_RW, &lomac_verbose_##level##, 1, "") +#include "kernel_log.h" + +/* sbuf_start() + * + * in: nothing + * out: nothing + * return: struct sbuf * to pass to later callers + * + */ + +lomac_log_t * +log_start(void) { + struct sbuf *s; + + s = sbuf_new(NULL, NULL, PATH_MAX * 2, 0); + KASSERT(s != NULL, ("sbuf uses M_WAITOK -- must not return NULL!")); + return (s); +} /* log_start() */ + + +/* log_append_string() + * + * in: s - a struct sbuf * + * in: data_s - null-terminated string to append to log + * out: nothing, see description for side-effects + * return: nothing + * + * This function appends `data_s' to `log_s', being careful to ensure + * that there is sufficient room in `log_s' for the data and a null + * terminator. If there is insufficient room in `log_s' for the entire + * `data_s' string, this function will append only the prefix of `data_s' + * which fits. + * + */ + +void +log_append_string(lomac_log_t *s, const char *data_s) { + + (void)sbuf_cat(s, data_s); +} /* log_append_string */ + + +/* log_append_int() + * + * in: data - integer value to append to log + * out: nothing, see description for side-effects + * return: nothing + * + * This function determines the ASCII representation of the integer + * value in `data' and, if there is sufficient room, appends this + * ASCII representation to `log_s'. If there is insufficient room, + * this function behaves as log_append_string(). + * + */ + +void +log_append_int(lomac_log_t *s, int data) { + + (void)sbuf_printf(s, "%d", data); +} /* log_append_int() */ + + +/* log_append_subject_id() + * + * in: p_subject - subject whose ID we want to append to the log message + * out: nothing, see description for side-effects + * return: nothing + * + * This function appends a string describing the identity of `p_subject' + * to `log_s'. If there is insufficient room in `log_s' for the entire + * ID string, only a (possibly empty) prefix of the ID string will be + * appended. + * + */ + +void +log_append_subject_id(lomac_log_t *s, const lomac_subject_t *p_subject) { + + (void)sbuf_printf(s, "p%dg%du%d:%s", p_subject->p_pid, + p_subject->p_pgrp->pg_id, p_subject->p_ucred->cr_uid, + p_subject->p_comm); +} /* log_append_subject_id() */ + +/* log_append_object_id() + * + * in: p_object - object whose ID we want to append to the log message + * out: nothing, see description for side-effects + * return: nothing + * + * This function appends a string describing the identity of `p_object' + * to `log_s'. If there is insufficient room in `log_s' for the entire + * ID string, only a (possibly empty) prefix of the ID string will be + * appended. + * + */ + +void +log_append_object_id(lomac_log_t *s, const lomac_object_t *p_object) { + struct lomac_node *ln; + + switch (p_object->lo_type) { + case LO_TYPE_UVNODE: + (void)sbuf_printf(s, "vp %p", p_object->lo_object.vnode); + break; + case LO_TYPE_LVNODE: + ln = VTOLOMAC(p_object->lo_object.vnode); +#ifdef LOMAC_DEBUG_INCNAME + (void)sbuf_printf(s, "named \"%s\"", ln->ln_name); +#else + if (ln->ln_entry != NULL) + (void)sbuf_printf(s, "named \"%s\"", + ln->ln_entry->ln_path); + else + (void)sbuf_printf(s, "under \"%s\"", + ln->ln_underpolicy->ln_path); +#endif + break; + case LO_TYPE_PIPE: + (void)sbuf_printf(s, "pipe %p", p_object->lo_object.pipe); + break; + case LO_TYPE_SOCKETPAIR: + (void)sbuf_printf(s, "socket %p", p_object->lo_object.socket); + break; + default: + panic("invalid LOMAC object type"); + } +} /* log_append_object_id() */ + +/* log_print() + * + * in: nothing + * out: nothing + * return: nothing + * + * This function prints `log_s' to the system log. + * + */ + +void +log_print(lomac_log_t *s) { + + sbuf_finish(s); + log(LOG_INFO, "%s", sbuf_data(s)); + sbuf_delete(s); +} /* log_print() */ diff --git a/sys/contrib/lomac/kernel_log.h b/sys/contrib/lomac/kernel_log.h new file mode 100644 index 0000000..66cf562 --- /dev/null +++ b/sys/contrib/lomac/kernel_log.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_log.h,v 1.5 2001/09/20 21:29:21 tfraser Exp $ + */ + +#ifndef _KERNEL_LOG_H_ +#define _KERNEL_LOG_H_ + +#include "kernel_interface.h" + +/* Use this unsigned int and its constants to set the log output * + * verbosity. Use of the log_* functions should be surrounded by * + * if statements of the form "if( verbose & VERBOSE_FOO )" where * + * VERBOSE_FOO is the constant below which corresponds to the * + * type of event you're logging. */ +#define VERBOSE_NOLOG 0x00000000 /* no log output, please. */ +#define VERBOSE_DEMOTE_DENY 0x00000001 /* log demotions and access denials. */ +#define VERBOSE_PIPE 0x00000002 /* log changes to pipe "levels". */ +#define VERBOSE_LOG_SOCKETS 0x00000004 /* log UNIX domain socket setup. */ +#define VERBOSE_LOG_OBJECTS 0x00000008 /* log opening of objects. */ +#ifdef TRUST +#define VERBOSE_TRUST 0x00000020 /* log when trust stops demotion. */ +#endif + +#ifndef VERBOSITY_SETTING +#define VERBOSITY_SETTING(level) extern unsigned int lomac_verbose_##level; +#endif +VERBOSITY_SETTING(demote_deny); +VERBOSITY_SETTING(log_sockets); +VERBOSITY_SETTING(log_objects); +#ifdef LOMAC_DEBUG_PIPE +VERBOSITY_SETTING(pipe); +#endif +#ifdef TRUST +VERBOSITY_SETTING(trust); +#endif + +lomac_log_t *log_start( void ); +void log_append_string( lomac_log_t *s, const char *data_s ); +void log_append_int( lomac_log_t *s, int data ); +void log_append_subject_id( lomac_log_t *s, const lomac_subject_t *p_subject ); +void log_append_object_id( lomac_log_t *s, const lomac_object_t *p_object ); +void log_print( lomac_log_t *s ); + +#endif diff --git a/sys/contrib/lomac/kernel_mediate.c b/sys/contrib/lomac/kernel_mediate.c new file mode 100644 index 0000000..c28ce00 --- /dev/null +++ b/sys/contrib/lomac/kernel_mediate.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_mediate.c,v 1.9 2001/10/17 15:19:40 bfeldman Exp $ + */ + +/* + * This file contains functions that make access control decisions + * concerning wether or not given system calls should be allowed + * or denied. This activity is called "mediation". These functions + * generally consider both the parameters passed to a system call + * and the current internal state of LOMAC in the course of making + * a decision. However, they do not modify these parameters or + * LOMAC's internal state. Functions for modifying LOMAC's internal + * state can be found in lomac_monitor.c. + * + */ + +#include "kernel_interface.h" +#include "kernel_mediate.h" +#if 0 +#include "lomac_plm.h" +#endif +#include "kernel_log.h" + +/* mediate_subject_level_subject() + * + * in: op_s - name of operation to mediate + * p_subject_one - subject one (for informational purposes only) + * level_one - already-known level of the first subject + * p_subject_two - subject two + * out: nothing + * return: value condition + * ----- --------- + * 0 caller should deny operation + * 1 caller should allow operation + * + * This function returns 1 if `p_subject_one's level is at least + * as great as `p_subject_two's level. Otherwise, it logs a permission + * failure on operation `op_s' and returns 0. + * + * This function is used to mediate pgrp changes. + * + */ + +int +mediate_subject_level_subject(const char *op_s, + const lomac_subject_t *p_subject_one, level_t level_one, + lomac_subject_t *p_subject_two) { + + lattr_t lattr_two; /* lattr of `p_subject_two' */ + int ret_val; /* result to return to caller */ + +#ifdef NO_MEDIATION + ret_val = 1; /* no denials, just logging */ +#else + ret_val = 0; /* pessimistically assume deny */ +#endif + + get_subject_lattr(p_subject_two, &lattr_two); + + if (lattr_two.level <= level_one) { + ret_val = 1; /* OK, allow */ + } else if (lomac_verbose_demote_deny) { + lomac_log_t *logmsg = log_start(); + + log_append_string(logmsg, "LOMAC: denied level-"); + log_append_int(logmsg, level_one); + log_append_string(logmsg, " proc "); + log_append_subject_id(logmsg, p_subject_one); + log_append_string(logmsg, " "); + log_append_string(logmsg, op_s); + log_append_string(logmsg, " to level-"); + log_append_int(logmsg, lattr_two.level); + log_append_string(logmsg, " proc "); + log_append_subject_id(logmsg, p_subject_two); + log_print(logmsg); + } + return (ret_val); +} /* mediate_subject_subject() */ + +/* mediate_subject_object() + * + * in: op_s - string describing operation, like "write" or "writev" + * p_subject - subject trying to operate on `p_object'. + * p_object - object that `p_subject' is trying to operate on. + * out: nothing + * return: value condition + * ----- --------- + * 0 Caller should prevent operation + * 1 Caller should permit operation + * + * This function returns 1 if the level of `p_object' is less than or + * equal to the level of `p_subject'. Otherwise, it returns 0 and + * logs a permission denial on `op_s'. + * + * This function allows LOMAC to mediate write and writev system calls. + * + */ + +int +mediate_subject_object(const char *op_s, lomac_subject_t *p_subject, + const lomac_object_t *p_object) { + lattr_t subject_lattr; /* lattr of `p_subject' */ + lattr_t object_lattr; /* lattr of `p_object' */ + int ret_val; /* value to return to caller */ + +#ifdef NO_MEDIATION + ret_val = 1; /* allow operation regardless of decision */ +#else + ret_val = 0; /* pessimistically assume deny */ +#endif + + /* Get the lattrs of `p_subject' and `p_object' so we can compare them. */ + get_subject_lattr(p_subject, &subject_lattr); + get_object_lattr(p_object, &object_lattr); + + /* + * If `p_subject's level is less than `p_object's level, + * we indicate that the operation must not be allowed. + */ + + if (!lomac_must_deny(&subject_lattr, &object_lattr) || + object_lattr.flags & LOMAC_ATTR_LOWWRITE) { + ret_val = 1; /* allow operation */ + } else if (lomac_verbose_demote_deny) { + lomac_log_t *logmsg = log_start(); + log_append_string(logmsg, "LOMAC: level-"); + log_append_int(logmsg, subject_lattr.level); + log_append_string(logmsg, " proc "); + log_append_subject_id(logmsg, p_subject); + log_append_string(logmsg, " denied "); + log_append_string(logmsg, op_s); + log_append_string(logmsg, " to level-"); + log_append_int(logmsg, object_lattr.level); + log_append_string(logmsg, " object "); + log_append_object_id(logmsg, p_object); + log_append_string(logmsg, "\n"); + log_print(logmsg); + } + return (ret_val); +} /* mediate_subject_object() */ + + +/* mediate_subject_object_open() + * + * in: p_subject - subject trying to operate on `p_object'. + * p_object - object that `p_subject' is trying to operate on. + * out: nothing + * return: value condition + * ----- --------- + * 0 Caller should prevent operation + * 1 Caller should permit operation + * + * This function returns 1 if the level of `p_object' is less than or + * equal to the level of `p_subject'. Otherwise, it returns 0 and + * logs a permission denial on `op_s'. + * + * This function allows LOMAC to mediate open system calls. + * + */ + +int +mediate_subject_object_open(lomac_subject_t *p_subject, + const lomac_object_t *p_object) { + lattr_t subject_lattr; /* lattr of `p_subject' */ + lattr_t object_lattr; /* lattr of `p_object' */ + int ret_val; /* value to return to caller */ + +#ifdef NO_MEDIATION + ret_val = 1; /* allow operation regardless of decision */ +#else + ret_val = 0; /* pessimistically assume deny */ +#endif + + /* Get the lattrs of `p_subject' and `p_object' so we can compare them. */ + get_subject_lattr(p_subject, &subject_lattr); + get_object_lattr(p_object, &object_lattr); + + /* + * If `p_subject's level is less than `p_object's level, + * we must indicate that the operation should not be allowed. + */ + if (lomac_must_deny(&subject_lattr, &object_lattr) && + object_lattr.flags & LOMAC_ATTR_LOWNOOPEN) { + if (lomac_verbose_demote_deny) { + lomac_log_t *logmsg = log_start(); + + log_append_string(logmsg, "LOMAC: level-"); + log_append_int(logmsg, subject_lattr.level); + log_append_string(logmsg, " proc "); + log_append_subject_id(logmsg, p_subject); + log_append_string(logmsg, " denied open to level-"); + log_append_int(logmsg, object_lattr.level); + log_append_string(logmsg, " object "); + log_append_object_id(logmsg, p_object); + log_append_string(logmsg, "\n"); + log_print(logmsg); + } + } else { + ret_val = 1; /* allow operation */ + } /* if/else allow/deny */ + return (ret_val); +} /* mediate_subject_object() */ + + +/* mediate_subject_at_level() + * + * in: op_s - name of operation being mediated + * p_subject - subject whose level we want to check + * target_level - level to compare to `p_subject's level + * + * out: nothing + * return: value condition + * ----- --------- + * 0 `p_subject' is not at `target_level' + * 1 `p_subject' is at `target_level' + * + * This function provides a predicate for determining whether or not + * `p_subject' is at the level specified by `target_level'. This + * function compares `p_subject's level to `target_level'. If the + * levels match, it retruns 1. Otherwise, it logs a permission denial + * on `op_s' and returns 0. + * + */ + +int +mediate_subject_at_level(const char *op_s, lomac_subject_t *p_subject, + const level_t target_level) { + lattr_t subject_lattr; /* lattr of `p_subject' */ + int ret_val; /* value returned to caller */ + +#ifdef NO_MEDIATION + ret_val = 1; /* allow operation regardless of decision */ +#else + ret_val = 0; /* pessimistically assume deny */ +#endif + + /* Make `subject_lattr' the lattr of `p_subject'. */ + get_subject_lattr(p_subject, &subject_lattr); + + /* compare with `target_lattr */ + if (subject_lattr.level == target_level) { + ret_val = 1; /* allow operation */ + } else if (lomac_verbose_demote_deny) { + lomac_log_t *logmsg = log_start(); + + log_append_string(logmsg, "LOMAC: denied level-"); + log_append_int(logmsg, subject_lattr.level); + log_append_string(logmsg, " proc "); + log_append_subject_id(logmsg, p_subject); + log_append_string(logmsg, "'s "); + log_append_string(logmsg, op_s); + log_append_string(logmsg, ".\n"); + log_print(logmsg); + } + return (ret_val); +} /* mediate_subject_at_level() */ diff --git a/sys/contrib/lomac/kernel_mediate.h b/sys/contrib/lomac/kernel_mediate.h new file mode 100644 index 0000000..2721292 --- /dev/null +++ b/sys/contrib/lomac/kernel_mediate.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_mediate.h,v 1.5 2001/09/26 21:18:00 bfeldman Exp $ + */ + +#ifndef _KERNEL_MEDIATE_H_ +#define _KERNEL_MEDIATE_H_ + +#include "kernel_interface.h" + +int mediate_subject_level_subject( const char *op_s, + const lomac_subject_t *p_subject_one, + level_t level_one, + lomac_subject_t *p_subject_two ); +int mediate_subject_object( const char *op_s, lomac_subject_t *p_subject, + const lomac_object_t *p_object ); +int mediate_subject_object_open( lomac_subject_t *p_subject, + const lomac_object_t *p_object ); +#if 0 +int mediate_subject_path( const char *op_s, const lomac_subject_t *p_subject, + const char *path_s ); +int mediate_path_path( const char *op_s, const lomac_subject_t *p_subject, + const char *canabsname_one_s, + const char *canabsname_two_s ); +#endif +int mediate_subject_at_level( const char *op_s, + lomac_subject_t *p_subject, + const level_t target_level ); +#if 0 +int mediate_object_at_level( const char *op_s, + const lomac_subject_t *p_subject, + const lomac_object_t *p_object, + const level_t target_level ); +#endif + +#endif diff --git a/sys/contrib/lomac/kernel_mmap.c b/sys/contrib/lomac/kernel_mmap.c new file mode 100644 index 0000000..c0217fb --- /dev/null +++ b/sys/contrib/lomac/kernel_mmap.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2001 Networks Associates Technology, Inc. + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: vm_mmap.c 1.6 91/10/21$ + * + * @(#)vm_mmap.c 8.4 (Berkeley) 1/12/94 + * $FreeBSD$ + * $Id: kernel_mmap.c,v 1.12 2001/10/17 15:34:29 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sysproto.h> +#include <sys/filedesc.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/fcntl.h> +#include <sys/conf.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> +#include <vm/vm_page.h> +#include <vm/vm_pager.h> + +#include "kernel_interface.h" +#include "kernel_mediate.h" +#include "kernel_monitor.h" +#include "kernel_util.h" +#include "lomacfs.h" + +extern int max_proc_mmap; + +int lomac_mmap(struct proc *, struct mmap_args *); + +/* + * Memory Map (mmap) system call. Note that the file offset + * and address are allowed to be NOT page aligned, though if + * the MAP_FIXED flag it set, both must have the same remainder + * modulo the PAGE_SIZE (POSIX 1003.1b). If the address is not + * page-aligned, the actual mapping starts at trunc_page(addr) + * and the return value is adjusted up by the page offset. + * + * Generally speaking, only character devices which are themselves + * memory-based, such as a video framebuffer, can be mmap'd. Otherwise + * there would be no cache coherency between a descriptor and a VM mapping + * both to the same character device. + * + * Block devices can be mmap'd no matter what they represent. Cache coherency + * is maintained as long as you do not write directly to the underlying + * character device. + */ +#ifndef _SYS_SYSPROTO_H_ +struct mmap_args { + void *addr; + size_t len; + int prot; + int flags; + int fd; + long pad; + off_t pos; +}; +#endif + +int +mmap(td, uap) + struct thread *td; + struct mmap_args *uap; +{ + struct proc *p = td->td_proc; + struct filedesc *fdp = p->p_fd; + struct file *fp = NULL; + struct vnode *vp, *origvp; + vm_offset_t addr; + vm_size_t size, pageoff; + vm_prot_t prot, maxprot; + void *handle; + int flags, error; + int disablexworkaround; + off_t pos; + struct vmspace *vms = p->p_vmspace; + vm_object_t obj; + lomac_object_t lobj; + + addr = (vm_offset_t) uap->addr; + size = uap->len; + prot = uap->prot & VM_PROT_ALL; + flags = uap->flags; + pos = uap->pos; + origvp = NULL; + + /* make sure mapping fits into numeric range etc */ + if ((ssize_t) uap->len < 0 || + ((flags & MAP_ANON) && uap->fd != -1)) + return (EINVAL); + + if (flags & MAP_STACK) { + if ((uap->fd != -1) || + ((prot & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE))) + return (EINVAL); + flags |= MAP_ANON; + pos = 0; + } + + /* + * Align the file position to a page boundary, + * and save its page offset component. + */ + pageoff = (pos & PAGE_MASK); + pos -= pageoff; + + /* Adjust size for rounding (on both ends). */ + size += pageoff; /* low end... */ + size = (vm_size_t) round_page(size); /* hi end */ + + /* + * Check for illegal addresses. Watch out for address wrap... Note + * that VM_*_ADDRESS are not constants due to casts (argh). + */ + if (flags & MAP_FIXED) { + /* + * The specified address must have the same remainder + * as the file offset taken modulo PAGE_SIZE, so it + * should be aligned after adjustment by pageoff. + */ + addr -= pageoff; + if (addr & PAGE_MASK) + return (EINVAL); + /* Address range must be all in user VM space. */ + if (VM_MAXUSER_ADDRESS > 0 && addr + size > VM_MAXUSER_ADDRESS) + return (EINVAL); +#ifndef i386 + if (VM_MIN_ADDRESS > 0 && addr < VM_MIN_ADDRESS) + return (EINVAL); +#endif + if (addr + size < addr) + return (EINVAL); + } + /* + * XXX for non-fixed mappings where no hint is provided or + * the hint would fall in the potential heap space, + * place it after the end of the largest possible heap. + * + * There should really be a pmap call to determine a reasonable + * location. + */ + else if (addr == 0 || + (addr >= round_page((vm_offset_t)vms->vm_taddr) && + addr < round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ))) + addr = round_page((vm_offset_t)vms->vm_daddr + MAXDSIZ); + + mtx_lock(&Giant); /* syscall marked mp-safe but isn't */ + if (flags & MAP_ANON) { + /* + * Mapping blank space is trivial. + */ + handle = NULL; + maxprot = VM_PROT_ALL; + pos = 0; + } else { + /* + * Mapping file, get fp for validation. Obtain vnode and make + * sure it is of appropriate type. + */ + if (((unsigned) uap->fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[uap->fd]) == NULL) { + mtx_unlock(&Giant); + return (EBADF); + } + if (fp->f_type != DTYPE_VNODE) { + mtx_unlock(&Giant); + return (EINVAL); + } + + /* + * don't let the descriptor disappear on us if we block + */ + fhold(fp); + + /* + * POSIX shared-memory objects are defined to have + * kernel persistence, and are not defined to support + * read(2)/write(2) -- or even open(2). Thus, we can + * use MAP_ASYNC to trade on-disk coherence for speed. + * The shm_open(3) library routine turns on the FPOSIXSHM + * flag to request this behavior. + */ + if (fp->f_flag & FPOSIXSHM) + flags |= MAP_NOSYNC; + vp = (struct vnode *) fp->f_data; + if (vp->v_type != VREG && vp->v_type != VCHR) { + error = EINVAL; + goto done; + } + if (vp->v_type == VREG) { + /* + * Get the proper underlying object + */ + if (VOP_GETVOBJECT(vp, &obj) != 0) { + error = EINVAL; + goto done; + } + origvp = vp; + vp = (struct vnode*)obj->handle; + } + /* + * XXX hack to handle use of /dev/zero to map anon memory (ala + * SunOS). + */ + if ((vp->v_type == VCHR) && + (vp->v_rdev->si_devsw->d_flags & D_MMAP_ANON)) { + handle = NULL; + maxprot = VM_PROT_ALL; + flags |= MAP_ANON; + pos = 0; + } else { + /* + * cdevs does not provide private mappings of any kind. + */ + /* + * However, for XIG X server to continue to work, + * we should allow the superuser to do it anyway. + * We only allow it at securelevel < 1. + * (Because the XIG X server writes directly to video + * memory via /dev/mem, it should never work at any + * other securelevel. + * XXX this will have to go + */ + if (securelevel >= 1) + disablexworkaround = 1; + else + disablexworkaround = suser(p); + if (vp->v_type == VCHR && disablexworkaround && + (flags & (MAP_PRIVATE|MAP_COPY))) { + error = EINVAL; + goto done; + } + /* + * Ensure that file and memory protections are + * compatible. Note that we only worry about + * writability if mapping is shared; in this case, + * current and max prot are dictated by the open file. + * XXX use the vnode instead? Problem is: what + * credentials do we use for determination? What if + * proc does a setuid? + */ + maxprot = VM_PROT_EXECUTE; /* ??? */ + if (fp->f_flag & FREAD) { + maxprot |= VM_PROT_READ; + } else if (prot & PROT_READ) { + error = EACCES; + goto done; + } + /* + * If we are sharing potential changes (either via + * MAP_SHARED or via the implicit sharing of character + * device mappings), and we are trying to get write + * permission although we opened it without asking + * for it, bail out. Check for superuser, only if + * we're at securelevel < 1, to allow the XIG X server + * to continue to work. + */ + + if ((flags & MAP_SHARED) != 0 || + (vp->v_type == VCHR && disablexworkaround)) { + if ((fp->f_flag & FWRITE) != 0) { + struct vattr va; + if ((error = + VOP_GETATTR(vp, &va, + p->p_ucred, td))) { + goto done; + } + if ((va.va_flags & + (SF_SNAPSHOT|IMMUTABLE|APPEND)) == 0) { + maxprot |= VM_PROT_WRITE; + } else if (prot & PROT_WRITE) { + error = EPERM; + goto done; + } + } else if ((prot & PROT_WRITE) != 0) { + error = EACCES; + goto done; + } + } else { + maxprot |= VM_PROT_WRITE; + } + + handle = (void *)vp; + origvp = vp; + } + } + + /* + * Do not allow more then a certain number of vm_map_entry structures + * per process. Scale with the number of rforks sharing the map + * to make the limit reasonable for threads. + */ + if (max_proc_mmap && + vms->vm_map.nentries >= max_proc_mmap * vms->vm_refcnt) { + error = ENOMEM; + goto done; + } + + mtx_unlock(&Giant); + error = 0; + if (handle != NULL && VISLOMAC(origvp)) { + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = origvp; + if (flags & MAP_SHARED && maxprot & VM_PROT_WRITE && + !mediate_subject_object("mmap", p, &lobj)) + error = EPERM; + if (error == 0 && maxprot & VM_PROT_READ) + error = monitor_read_object(p, &lobj); + } + if (error == 0) + error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot, + flags, handle, pos); + if (error == 0) + td->td_retval[0] = (register_t) (addr + pageoff); + mtx_lock(&Giant); +done: + if (fp) + fdrop(fp, td); + mtx_unlock(&Giant); + return (error); +} + +static void +vm_drop_perms_recurse(struct thread *td, struct vm_map *map, lattr_t *lattr) { + struct vm_map_entry *vme; + + for (vme = map->header.next; vme != &map->header; vme = vme->next) { + if (vme->eflags & MAP_ENTRY_IS_SUB_MAP) { + vm_map_lock_read(vme->object.sub_map); + vm_drop_perms_recurse(td, vme->object.sub_map, + lattr); + vm_map_unlock_read(vme->object.sub_map); + continue; + } + if ((vme->eflags & (MAP_ENTRY_COW | MAP_ENTRY_NOSYNC)) == 0 && + vme->max_protection & VM_PROT_WRITE) { + vm_object_t object; + vm_ooffset_t offset; + lomac_object_t lobj; + struct vnode *vp; + lattr_t olattr; + + offset = vme->offset; + object = vme->object.vm_object; + if (object == NULL) + continue; + while (object->backing_object) { + object = object->backing_object; + offset += object->backing_object_offset; + } + /* + * Regular objects (swap, etc.) inherit from + * their creator. Vnodes inherit from their + * underlying on-disk object. + */ + if (object->type == OBJT_DEVICE) + continue; + if (object->type == OBJT_VNODE) { + vp = lobj.lo_object.vnode = object->handle; + /* + * For the foreseeable future, an OBJT_VNODE + * is always !VISLOMAC(). + */ + lobj.lo_type = VISLOMAC(vp) ? + LO_TYPE_LVNODE : LO_TYPE_UVNODE; + } else { + vp = NULL; + lobj.lo_object.vm_object = object; + lobj.lo_type = LO_TYPE_VM_OBJECT; + } + get_object_lattr(&lobj, &olattr); + /* + * Revoke write access only to files with a higher + * level than the process or which have a possibly- + * undeterminable level (interpreted as "lowest"). + */ + if (lomac_must_deny(lattr, &olattr)) + continue; + vm_map_lock_upgrade(map); + /* + * If it's a private, non-file-backed mapping and + * not mapped anywhere else, we can just take it + * down with us. + */ + if (vp == NULL && object->flags & OBJ_ONEMAPPING) { + olattr.level = lattr->level; + set_object_lattr(&lobj, olattr); + goto downgrade; + } + if ((vme->protection & VM_PROT_WRITE) == 0) + vme->max_protection &= ~VM_PROT_WRITE; + else { + vm_object_reference(object); + if (vp != NULL) + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, + td); + vm_object_page_clean(object, + OFF_TO_IDX(offset), + OFF_TO_IDX(offset + vme->end - vme->start + + PAGE_MASK), + OBJPC_SYNC); + if (vp != NULL) + VOP_UNLOCK(vp, 0, td); + vm_object_deallocate(object); + vme->eflags |= MAP_ENTRY_COW | + MAP_ENTRY_NEEDS_COPY; + pmap_protect(map->pmap, vme->start, vme->end, + vme->protection & ~VM_PROT_WRITE); + vm_map_simplify_entry(map, vme); + } + downgrade: + vm_map_lock_downgrade(map); + } + } +} + +void +kernel_vm_drop_perms(struct thread *td, lattr_t *newlattr) { + struct vm_map *map = &td->td_proc->p_vmspace->vm_map; + + mtx_lock(&Giant); + vm_map_lock_read(map); + vm_drop_perms_recurse(td, map, newlattr); + vm_map_unlock_read(map); + mtx_unlock(&Giant); +} + +/* + * Take the level of new vm_objects from the parent subject's level. + */ +static void +vm_object_init_lattr(vm_object_t object) { + lomac_object_t lobj; + lattr_t lattr; + + get_subject_lattr(curthread->td_proc, &lattr); + lattr.flags = 0; + lobj.lo_type = LO_TYPE_VM_OBJECT; + lobj.lo_object.vm_object = object; + set_object_lattr(&lobj, lattr); +} + + +#define PGO_ALLOC_REPLACEMENT(n) \ +static vm_object_t (*old_pgo_alloc_##n)(void *, vm_ooffset_t, \ + vm_prot_t, vm_ooffset_t); \ + \ +static vm_object_t \ +pgo_alloc_##n(void *handle, vm_ooffset_t size, vm_prot_t prot, \ + vm_ooffset_t off) { \ + vm_object_t newobj = NULL; \ + \ + newobj = old_pgo_alloc_##n(handle, size, prot, off); \ + if (newobj != NULL) \ + vm_object_init_lattr(newobj); \ + return (newobj); \ +} + +#define PGO_ALLOC_REPLACE(n) \ + do { \ + old_pgo_alloc_##n = pagertab[n]->pgo_alloc; \ + if (pagertab[n]->pgo_alloc != NULL) \ + pagertab[n]->pgo_alloc = pgo_alloc_##n; \ + } while (0) + +#define PGO_ALLOC_UNREPLACE(n) \ + do { \ + pagertab[n]->pgo_alloc = old_pgo_alloc_##n; \ + } while (0) + +PGO_ALLOC_REPLACEMENT(0); +PGO_ALLOC_REPLACEMENT(1); +PGO_ALLOC_REPLACEMENT(2); +PGO_ALLOC_REPLACEMENT(3); +PGO_ALLOC_REPLACEMENT(4); +PGO_ALLOC_REPLACEMENT(5); + +extern int npagers; + +int +lomac_initialize_vm(void) { + GIANT_REQUIRED; + + if (npagers != 6) { + printf("LOMAC: number of pagers %d not expected 6!\n", npagers); + return (EDOM); + } + PGO_ALLOC_REPLACE(0); + PGO_ALLOC_REPLACE(1); + PGO_ALLOC_REPLACE(2); + PGO_ALLOC_REPLACE(3); + PGO_ALLOC_REPLACE(4); + PGO_ALLOC_REPLACE(5); + return (0); +} + +int +lomac_uninitialize_vm(void) { + GIANT_REQUIRED; + + PGO_ALLOC_UNREPLACE(0); + PGO_ALLOC_UNREPLACE(1); + PGO_ALLOC_UNREPLACE(2); + PGO_ALLOC_UNREPLACE(3); + PGO_ALLOC_UNREPLACE(4); + PGO_ALLOC_UNREPLACE(5); + return (0); +} diff --git a/sys/contrib/lomac/kernel_monitor.c b/sys/contrib/lomac/kernel_monitor.c new file mode 100644 index 0000000..199b8a0 --- /dev/null +++ b/sys/contrib/lomac/kernel_monitor.c @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_monitor.c,v 1.12 2001/10/16 15:25:02 bfeldman Exp $ + */ + +/* + * This file contains functions which update LOMAC's internal + * state in response to system events, such as successful + * system calls. These updates allow LOMAC to keep an accurate + * picture of the kernel's state, enabling LOMAC to make reasonable + * decisions when it mediates processes' use of security-relevant + * system calls. These functions perform no mediation themselves - + * that is, they do not make access control decisions concerning + * whether a given system call should be allowed or denied. This + * mediation is handled by the functions in lomac_mediate.c. + */ + +#include "kernel_interface.h" +#include "kernel_monitor.h" +#include "kernel_log.h" + +#include "kernel_util.h" + + +/* monitor_read_object() + * + * in: p_subject - subject that read `p_object'. + * p_object - object read by `p_subject'. + * out: nothing + * return: nothing + * + * This function examines the objects read by subjects. If a subject + * reads from an object with a level lower than its own, this function + * reduces the subject's level to match the object's. This lowering + * is referred to as "demotion" in much of the LOMAC documentation. + * This function performs no mediation. + * + * This function is for the following kinds of objects: + * regular files and FIFOs. + * It is also used to monitor reads on unnamed pipes. LOMAC does not + * consider unnamed pipes to be objects, and treats them differently + * from objects such as files. However, the differences are mostly + * in the write-handling behavior. LOMAC's read-handling behavior is + * the same both for objects and unnamed pipes, so this function + * handles both cases. + */ + +int +monitor_read_object(lomac_subject_t *p_subject, lomac_object_t *p_object) { + lattr_t subject_lattr; /* lattr of `p_subject' */ + lattr_t object_lattr; /* lattr of `p_object' */ + + /* Get the lattrs of `p_subject' and `p_object' so we can compare them. */ + get_subject_lattr(p_subject, &subject_lattr); + get_object_lattr(p_object, &object_lattr); + + /* + * If `p_object's level is less than `p_subject's level, + * we must demote `p_subject'. The level may be 0 to indicate + * existence before LOMAC started. + */ + if (object_lattr.level && + lomac_must_demote(&subject_lattr, &object_lattr) && + (subject_lattr.flags & LOMAC_ATTR_NODEMOTE) == 0) { + if (subject_do_not_demote(p_subject)) + return (0); + set_subject_lattr(p_subject, object_lattr); /* demote! */ + if (lomac_verbose_demote_deny) { + lomac_log_t *s = log_start(); + + log_append_string(s, "LOMAC: level-"); + log_append_int(s, subject_lattr.level); + log_append_string(s, " subject "); + log_append_subject_id(s, p_subject); + log_append_string(s, " demoted to level "); + log_append_int(s, object_lattr.level); + log_append_string(s, " after reading "); + log_append_object_id(s, p_object); + log_append_string(s, "\n"); + log_print(s); + } + } /* if we need to demote */ + return (0); +} /* monitor_read_object() */ + + +/* monitor_pipe_write() + * + * in: p_subject - subject that just wrote to `p_pipe'. + * p_pipe - pipe `p_subject' has written to. + * out: p_pipe - pipe may have its level adjusted. + * return: 0 + * + * This function should be called after a successful write to + * `p_pipe'. If the level of `p_subject' is less than the level of + * `p_pipe', this function reduces `p_pipe's level to match + * `current's. + * + * + */ + +int +monitor_pipe_write(lomac_subject_t *p_subject, lomac_object_t *p_pipe) { + lattr_t pipe_lattr; /* lattr of `p_pipe' */ + lattr_t subject_lattr; /* lattr of `p_subject' */ + + get_subject_lattr(p_subject, &subject_lattr); + get_object_lattr(p_pipe, &pipe_lattr); + if (lomac_must_demote(&pipe_lattr, &subject_lattr)) { + subject_lattr.flags = 0; + set_object_lattr(p_pipe, subject_lattr); +#ifdef LOMAC_DEBUG_PIPE + if (lomac_verbose_pipe) { + lomac_log_t *s = log_start(); + + log_append_string(s, "LOMAC: level-"); + log_append_int(s, subject_lattr.level); + log_append_string(s, " subject "); + log_append_subject_id(s, p_subject); + log_append_string(s, " contaminated level-"); + log_append_int(s, pipe_lattr.level); + log_append_string(s, " "); + log_append_object_id(s, p_pipe); + log_append_string(s, "\n"); + log_print(s); + } +#endif /* LOMAC_DEBUG_PIPE */ + } + return (0); +} /* monitor_pipe_write() */ + + +/* monitor_read_net_socket() + * + * in: p_subject - subject that read from network socket. + * out: nothing + * return: 0 + * + * + */ + +int +monitor_read_net_socket(lomac_subject_t *p_subject) { + lattr_t subject_lattr; /* lattr of `p_subject' */ + lattr_t socket_lattr; /* lattr of socket (always lowest) */ + + socket_lattr.level = LOMAC_LOWEST_LEVEL; + socket_lattr.flags = 0; + get_subject_lattr(p_subject, &subject_lattr); + + if (lomac_must_demote(&subject_lattr, &socket_lattr) && + (subject_lattr.flags & + (LOMAC_ATTR_NODEMOTE | LOMAC_ATTR_NONETDEMOTE)) == 0) { + + if (subject_do_not_demote(p_subject)) + return (0); + socket_lattr.flags = subject_lattr.flags; + set_subject_lattr(p_subject, socket_lattr); /* demote! */ + if (lomac_verbose_demote_deny) { + lomac_log_t *s = log_start(); + + log_append_string(s, "LOMAC: level-"); + log_append_int(s, subject_lattr.level); + log_append_string(s, " subject "); + log_append_subject_id(s, p_subject); + log_append_string(s, " demoted to level "); + log_append_int(s, socket_lattr.level); + log_append_string(s, " after reading from " + "the network\n"); + log_print(s); + } + } + return (0); +} diff --git a/sys/contrib/lomac/kernel_monitor.h b/sys/contrib/lomac/kernel_monitor.h new file mode 100644 index 0000000..e2bab47 --- /dev/null +++ b/sys/contrib/lomac/kernel_monitor.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_monitor.h,v 1.5 2001/09/26 23:00:44 tfraser Exp $ + */ + +#ifndef _LOMAC_MONITOR_H_ +#define _LOMAC_MONITOR_H_ + +int monitor_read_object( lomac_subject_t *, lomac_object_t * ); +int monitor_pipe_write( lomac_subject_t *, lomac_object_t * ); +int monitor_read_net_socket( lomac_subject_t *p_subject ); + + +#if 0 +void monitor_open( lomac_subject_t *p_subject, lomac_object_t *p_object ); +void monitor_pipe_create( lomac_subject_t *p_subject, + lomac_object_t *p_pipe ); +void monitor_unix_socket_bind( lomac_subject_t *p_subject, + lomac_object_t *p_socket, + lomac_object_t *p_name ); +void monitor_unix_socket_abstract( lomac_subject_t *p_subject, + lomac_object_t *p_socket ); +void monitor_unix_socketpair( lomac_subject_t *p_subject, + lomac_object_t *p_socket1, + lomac_object_t *p_socket2 ); +void monitor_unix_socket_accept_connect( lomac_subject_t *p_subject, + lomac_object_t *p_old_socket, + lomac_object_t *p_new_socket ); +#endif +#endif diff --git a/sys/contrib/lomac/kernel_pipe.c b/sys/contrib/lomac/kernel_pipe.c new file mode 100644 index 0000000..c85b0ca --- /dev/null +++ b/sys/contrib/lomac/kernel_pipe.c @@ -0,0 +1,235 @@ +/************************************************************************* + * + * kernel_pipe.c + * + * LOMAC - Low Water-Mark Mandatory Access Control + * Copyright (c) 1999-2001 Networks Associates, Inc. All rights reserved. + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of NAI Labs, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * + * This file contains part of LOMAC's interface to the kernel. This + * part allows LOMAC to monitor (unnamed) pipe read and write + * operations by interposing control on the kernel's pipeops vector. + * + * The pipeops vector is defined in kern/sys_pipe.c. + * + * USAGE: + * + * The LOMAC LKM should call lomac_initialize_pipes() at LKM load time. + * This function turns unnamed pipe interposition on by modifying + * the function addresses in pipeops. + * + * Once the LOMAC LKM turns interposition on, all reads and writes + * will pass through this file's monitoring functions. + * + * This file provides a lomac_uninitialize_pipes() function which + * turns unnamed pipe interposition off by restoring pipeops to + * its original unmodified state. Once the LOMAC LKM turns + * interposition off, subsequent unnamed pipe reads and writes + * will not pass through this file's monitoring functions. + * + * HOW LOMAC HANDLES PIPES: + * + * (This text describes how LOMAC handles (unnamed) pipes in terms of + * abstract architecture-independent concepts.) LOMAC does not treat + * pipes as objects, as it does files. When the kernel creates a new + * pipe, LOMAC assigns it the highest level. Whenever a process + * writes to the pipe, LOMAC reduces the pipe's level to match the + * level of the writing process. Whenever a process reads from a + * pipe, LOMAC reduces the level of the reading process to match the + * pipe's level. As a result, if a high-level process reads the + * output of a low-level process through a pipe, the reading process + * will wind up at the low level. + * + * It takes two `struct pipe's to make a pipe. We set the level + * information in both `struct pipes', and keep them synchronized. + * + * This code presently relies on the one-big-kernel-lock to + * synchronize its access to the `pipe_state' field of each `struct + * pipe'. + * + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/file.h> +#include <sys/selinfo.h> +#include <sys/pipe.h> +#include <sys/proc.h> +#include <sys/uio.h> + +#include "lomac.h" +#include "kernel_interface.h" +#include "kernel_monitor.h" +#include "kernel_pipe.h" + + +/* `pipeops' is the kernel's pipe operations vector for the file * + * structure. All reads and writes to pipes call through this vector. */ +extern struct fileops pipeops; /* defined in kern/sys_pipe.c */ + + +/* These vars store the original addresses of the pipeops read and * + * write operations, so we can call them, and even restore them * + * later if we want to. */ +static int (*pipe_read_orig)(struct file *, struct uio *, struct ucred *, + int, struct thread *); +static int (*pipe_write_orig)(struct file *, struct uio *, struct ucred *, + int, struct thread *); + + +/* declarations of functions private to this module: */ +static int lomac_pipe_read(struct file *, struct uio *, struct ucred *, + int, struct thread *); +static int lomac_pipe_write(struct file *, struct uio *, struct ucred *, + int, struct thread *); + +/* -------------------- public functions ---------------------------- */ + +/* lomac_initialize_pipes() + * + * in: nothing + * out: nothing + * return: 0 + * + * Turns pipe interposition on by replacing the pipe_read() and pipe_write() + * operations in the kernel's pipeops vector with lomac_pipe_read() and + * lomac_pipe_write(). Saves the addresses of the original operations + * so other functions can call them, and so pipe_interposition_off() + * can restore the pipeops vector to its original unmodified state. + * + */ + +int +lomac_initialize_pipes(void) { + + pipe_read_orig = pipeops.fo_read; + pipeops.fo_read = lomac_pipe_read; + pipe_write_orig = pipeops.fo_write; + pipeops.fo_write = lomac_pipe_write; + return (0); +} /* lomac_initialize_pipes() */ + + +/* lomac_uninitialize_pipes() + * + * in: nothing + * out: nothing + * return: 0 + * + * Turns pipe interposition off by restoring the pipeops vector to its + * original unmodified state. + * + * See note at top of file regarding this function and unloading the + * LOMAC LKM. + * + */ + +int +lomac_uninitialize_pipes(void) { + + KASSERT(pipe_read_orig, ("LOMAC:pipe interpositon off before on")); + KASSERT(pipe_write_orig, ("LOMAC:pipe interpositon off before on")); + pipeops.fo_read = pipe_read_orig; + pipeops.fo_write = pipe_write_orig; + return (0); +} /* lomac_uninitialize_pipes() */ + + + +/* ------------------- private functions --------------------------- */ + +#ifndef MIN +#define MIN(lo, mac) ((lo) < (mac) ? (lo) : (mac)) +#endif + +/* lomac_pipe_read() + * + * Passes the read operation down to pipe_read_orig(). If + * pipe_read_orig() returns success, examines the level of the pipe + * and the reading process. If the reading process has a higher + * level, reduces the level of the process to equal the pipe's level. + * + */ + +static int +lomac_pipe_read(struct file *fp, struct uio *uio, struct ucred *cred, + int flags, struct thread *td) { + lomac_object_t read_pipe; /* attrs are in read end of pipe */ + struct uio kuio; + struct iovec kiov; + void *buf; + int len; + int ret_val; /* holds return values */ + + len = MIN(uio->uio_resid, BIG_PIPE_SIZE); + kiov.iov_base = buf = malloc(len, M_TEMP, M_WAITOK); + kiov.iov_len = len; + kuio.uio_iov = &kiov; + kuio.uio_iovcnt = 1; + kuio.uio_offset = 0; + kuio.uio_resid = len; + kuio.uio_segflg = UIO_SYSSPACE; + kuio.uio_rw = UIO_READ; + kuio.uio_td = td; + ret_val = pipe_read_orig(fp, &kuio, cred, flags, td); + if (ret_val == 0) { + read_pipe.lo_type = LO_TYPE_PIPE; + read_pipe.lo_object.pipe = (struct pipe *)fp->f_data; + (void)monitor_read_object(td->td_proc, &read_pipe); + ret_val = uiomove(buf, len - kuio.uio_resid, uio); + } + free(buf, M_TEMP); + return (ret_val); +} /* lomac_pipe_read() */ + + +/* lomac_pipe_write() + * + * Passes the write operation down to pipe_write_orig(). If + * pipe_write_orig() returns success, examines the level of the pipe + * and the writing process. If the pipe has a higher level than the + * writing process, this function reduces the pipe's level to equal + * the level of the writing process. + * + */ + +static int +lomac_pipe_write(struct file *fp, struct uio *uio, struct ucred *cred, + int flags, struct thread *td) { + lomac_object_t pipe; + int ret_val; /* holds return values */ + + pipe.lo_type = LO_TYPE_PIPE; + pipe.lo_object.pipe = (struct pipe *)fp->f_data; + ret_val = monitor_pipe_write(td->td_proc, &pipe); + if (ret_val == 0) + ret_val = pipe_write_orig(fp, uio, cred, flags, td); + + return (ret_val); +} /* lomac_pipe_write() */ diff --git a/sys/contrib/lomac/kernel_pipe.h b/sys/contrib/lomac/kernel_pipe.h new file mode 100644 index 0000000..5b566f6 --- /dev/null +++ b/sys/contrib/lomac/kernel_pipe.h @@ -0,0 +1,45 @@ +#ifndef _KERNEL_PIPE_H_ +#define _KERNEL_PIPE_H_ + +/****************************************************************** + * + * kernel_pipe.h + * + * LOMAC - Low Water-Mark Mandatory Access Control + * Copyright (c) 1999-2001 Networks Associates, Inc. All rights reserved. + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of NAI Labs, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: kernel_pipe.h,v 1.1 2001/09/20 21:29:21 tfraser Exp $ + * + * Public interface to kernel_pipe.c, the part of the LOMAC kernel + * interface that takes control of operations on unnamed pipes. + * + */ + +int lomac_initialize_pipes( void ); +int lomac_uninitialize_pipes( void ); + + +#endif /* _KERNEL_PIPE_H_ */ diff --git a/sys/contrib/lomac/kernel_plm.c b/sys/contrib/lomac/kernel_plm.c new file mode 100644 index 0000000..38777c3 --- /dev/null +++ b/sys/contrib/lomac/kernel_plm.c @@ -0,0 +1,373 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_plm.c,v 1.9 2001/10/17 15:20:09 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/queue.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/namei.h> + +#include "kernel_interface.h" +#include "kernel_plm.h" +#include "lomacfs.h" +#include "policy_plm.h" + +MALLOC_DEFINE(M_LOMACPLM, "LOMAC_PLM", "LOMAC PLM nodes and strings"); +char *strsep(register char **stringp, register const char *delim); + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(stringp, delim) + register char **stringp; + register const char *delim; +{ + register char *s; + register const char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +struct lomac_node_entry lomac_node_entry_root = { + SLIST_HEAD_INITIALIZER(lomac_node_entry), + { NULL }, + LN_HIGHEST_LEVEL | LN_INHERIT_HIGH, + "/" +}; + +static struct lomac_node_entry * +lomac_plm_subtree_find_cnp(struct lomac_node_entry *root, + struct componentname *cnp) { + char *nameptr = cnp->cn_nameptr; + struct lomac_node_entry *lne; + int len = cnp->cn_namelen; + + SLIST_FOREACH(lne, &root->ln_children, ln_chain) + if (strlen(lne->ln_name) == len && + bcmp(lne->ln_name, nameptr, len) == 0) + break; + + return (lne); +} + +static struct lomac_node_entry * +lomac_plm_subtree_find(struct lomac_node_entry *root, const char *name) { + struct lomac_node_entry *lne; + + SLIST_FOREACH(lne, &root->ln_children, ln_chain) + if (strcmp(name, lne->ln_name) == 0) + break; + + return (lne); +} + + +/* + * This is called from inside getnewvnode() before the vnode is in use. + */ +void +lomac_plm_init_lomacfs_vnode(struct vnode *dvp, struct vnode *vp, + struct componentname *cnp, lattr_t *subjlattr) { + struct lomac_node *ln = VTOLOMAC(vp); + struct lomac_node_entry *mlne = NULL; + + /* + * Only "/" has no parent, so inherit directly from our PLM root. + */ + if (dvp == NULL) { + ln->ln_flags = lomac_node_entry_root.ln_flags; + ln->ln_entry = ln->ln_underpolicy = &lomac_node_entry_root; + } else { + struct lomac_node *dln = VTOLOMAC(dvp); + struct lomac_node_entry *dlne = dln->ln_entry; + int fixup_inherit = 0; + + /* + * If we have no directory-specific entry, we inherit + * directly from the lomac_node's previously-inherited + * flags implicitly, otherwise we inherit explicitly + * from the corresponding lomac_node_entry. + */ + if (dlne == NULL) { + ln->ln_flags = dln->ln_flags & LN_INHERIT_MASK; + fixup_inherit = 1; + ln->ln_underpolicy = dln->ln_underpolicy; + ln->ln_entry = NULL; + } else if ((mlne = lomac_plm_subtree_find_cnp(dlne, cnp)) == + NULL) { + ln->ln_flags = dlne->ln_flags & LN_INHERIT_MASK; + fixup_inherit = 2; + ln->ln_underpolicy = dlne; + ln->ln_entry = NULL; + } else { + ln->ln_entry = ln->ln_underpolicy = mlne; + } + if (fixup_inherit) { + switch (ln->ln_flags) { + case LN_INHERIT_LOW: + ln->ln_flags |= LN_LOWEST_LEVEL; + break; + case LN_INHERIT_SUBJ: + if (subjlattr->level == LOMAC_HIGHEST_LEVEL) + ln->ln_flags |= LN_HIGHEST_LEVEL; + else { + ln->ln_flags &= ~LN_INHERIT_MASK; + ln->ln_flags |= LN_INHERIT_LOW | + LN_LOWEST_LEVEL; + } + break; + case LN_INHERIT_HIGH: + ln->ln_flags |= LN_HIGHEST_LEVEL; + break; + } + if (fixup_inherit == 2) + ln->ln_flags |= + (dlne->ln_flags & LN_CHILD_ATTR_MASK) >> + LN_CHILD_ATTR_SHIFT; + } else { + /* this is the only case where mlne != NULL */ + ln->ln_flags &= ~(LN_INHERIT_MASK | LN_ATTR_MASK); + ln->ln_flags |= mlne->ln_flags & + (LN_INHERIT_MASK | LN_ATTR_MASK); + if ((mlne->ln_flags & LN_LEVEL_MASK) == + LN_SUBJ_LEVEL) { + if (subjlattr->level == LOMAC_HIGHEST_LEVEL) + ln->ln_flags |= LN_HIGHEST_LEVEL; + else + ln->ln_flags |= LN_LOWEST_LEVEL; + } else + ln->ln_flags |= mlne->ln_flags & LN_LEVEL_MASK; + } + } + + KASSERT(ln->ln_flags & LN_LEVEL_MASK, ("lomac_node has no level")); + KASSERT(ln->ln_flags & LN_INHERIT_MASK, ("lomac_node has no inherit")); +#ifdef INVARIANTS + if (mlne != NULL) { + KASSERT(mlne->ln_flags & LN_LEVEL_MASK, + ("lomac_node_entry has no level")); + KASSERT(mlne->ln_flags & LN_INHERIT_MASK, + ("lomac_node_entry has no inherit")); + } +#endif /* INVARIANTS */ +} + +static struct lomac_node_entry * +lomac_plm_subtree_new(struct lomac_node_entry *plne, char *name) { + struct lomac_node_entry *lne; + static struct lomac_node_entry_head head_init = + SLIST_HEAD_INITIALIZER(lomac_node_entry); + + lne = malloc(sizeof(*lne), M_LOMACPLM, M_WAITOK); + bcopy(&head_init, &lne->ln_children, sizeof(head_init)); + lne->ln_name = name; + lne->ln_flags = plne->ln_flags & LN_INHERIT_MASK; + switch (lne->ln_flags) { + case LN_INHERIT_LOW: + lne->ln_flags |= LN_LOWEST_LEVEL; + break; + case LN_INHERIT_HIGH: + lne->ln_flags |= LN_HIGHEST_LEVEL; + break; + case LN_INHERIT_SUBJ: + lne->ln_flags |= LN_SUBJ_LEVEL; + break; + } + SLIST_INSERT_HEAD(&plne->ln_children, lne, ln_chain); + return (lne); +} + +static void +lomac_plm_subtree_free(struct lomac_node_entry *lneself) { + struct lomac_node_entry_head *head = &lneself->ln_children; + struct lomac_node_entry *lne; + + while (!SLIST_EMPTY(head)) { + lne = SLIST_FIRST(head); + SLIST_REMOVE_HEAD(head, ln_chain); + lomac_plm_subtree_free(lne); + } + free(lneself, M_LOMACPLM); +} + +struct string_list { + SLIST_ENTRY(string_list) entries; + char string[1]; +}; +static SLIST_HEAD(, string_list) string_list_head = + SLIST_HEAD_INITIALIZER(string_list); + +static char * +string_list_new(const char *s) { + struct string_list *sl; + + sl = malloc(sizeof(*sl) + strlen(s), M_LOMACPLM, M_WAITOK); + strcpy(sl->string, s); + SLIST_INSERT_HEAD(&string_list_head, sl, entries); + + return (sl->string); +} + +static void +lomac_plm_uninitialize(void) { + struct lomac_node_entry_head *head = &lomac_node_entry_root.ln_children; + struct lomac_node_entry *lne; + struct string_list *sl; + + while (!SLIST_EMPTY(head)) { + lne = SLIST_FIRST(head); + SLIST_REMOVE_HEAD(head, ln_chain); + lomac_plm_subtree_free(lne); + } + while (!SLIST_EMPTY(&string_list_head)) { + sl = SLIST_FIRST(&string_list_head); + SLIST_REMOVE_HEAD(&string_list_head, entries); + free(sl, M_LOMACPLM); + } +} + +static int +lomac_plm_initialize(void) { + struct lomac_node_entry *plne, *lne; + plm_rule_t *pr; + + for (pr = plm; pr->path != NULL; pr++) { + char *path; + char *comp; + int depth; + + if (*pr->path == '\0') { + printf("lomac_plm: invalid path \"%s\"\n", pr->path); + return (EINVAL); + } + path = string_list_new(pr->path); + lne = &lomac_node_entry_root; + depth = 0; + for (;; depth++) { + plne = lne; + comp = strsep(&path, "/"); + if (comp == NULL) + break; + if (depth == 0) { /* special case: beginning / */ + if (*comp == '\0') + continue; + else { + printf("lomac_plm: not absolute path " + "\"%s\"\n", pr->path); + return (EINVAL); + } + } else if (depth == 1) { /* special case: "/" */ + if (*comp == '\0' && strsep(&path, "/") == NULL) + break; + } + if (*comp == '\0' || + strcmp(comp, ".") == 0 || + strcmp(comp, "..") == 0) { + printf("lomac_plm: empty path component in " + "\"%s\"\n", pr->path); + return (EINVAL); + } + lne = lomac_plm_subtree_find(plne, comp); + if (lne == NULL) { + lne = lomac_plm_subtree_new(plne, comp); + lne->ln_path = plne->ln_path; + } + } + lne->ln_path = pr->path; + if (pr->flags == PLM_NOFLAGS) + lne->ln_flags &= ~LN_LEVEL_MASK; + else + lne->ln_flags &= ~LN_INHERIT_MASK; + lne->ln_flags |= + plm_levelflags_to_node_flags[pr->level][pr->flags]; + if (pr->flags == PLM_NOFLAGS) + lne->ln_flags |= pr->attr; + else + lne->ln_flags |= (pr->attr & LN_ATTR_MASK) + << LN_CHILD_ATTR_SHIFT; + } + return (0); +} + +int lomac_plm_initialized = 0; + +static int +lomac_plm_modevent(module_t module, int event, void *unused) { + int error = 0; + + switch ((enum modeventtype)event) { + case MOD_LOAD: + error = lomac_plm_initialize(); + if (error == 0) + lomac_plm_initialized = 1; + break; + case MOD_UNLOAD: + lomac_plm_uninitialize(); + case MOD_SHUTDOWN: + break; + } + return (error); +} + +static moduledata_t lomac_plm_moduledata = { + "lomac_plm", + &lomac_plm_modevent, + NULL +}; +DECLARE_MODULE(lomac_plm, lomac_plm_moduledata, SI_SUB_VFS, SI_ORDER_ANY); +MODULE_VERSION(lomac_plm, 1); diff --git a/sys/contrib/lomac/kernel_plm.h b/sys/contrib/lomac/kernel_plm.h new file mode 100644 index 0000000..16db2dd --- /dev/null +++ b/sys/contrib/lomac/kernel_plm.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_plm.h,v 1.4 2001/09/20 17:47:46 bfeldman Exp $ + */ +#ifndef KERNEL_PLM_H +#define KERNEL_PLM_H + +extern int lomac_plm_initialized; /* set to 1 if successfully initialized */ +void lomac_plm_init_lomacfs_vnode(struct vnode *dvp, struct vnode *vp, + struct componentname *cnp, lattr_t *subjlattr); + +#endif /* KERNEL_PLM_H */ diff --git a/sys/contrib/lomac/kernel_socket.c b/sys/contrib/lomac/kernel_socket.c new file mode 100644 index 0000000..ebaf15d --- /dev/null +++ b/sys/contrib/lomac/kernel_socket.c @@ -0,0 +1,772 @@ +/* + * LOMAC - Low Water-Mark Mandatory Access Control + * Copyright (c) 2001 Networks Associates Technology, Inc. + * Copyright (c) 1982, 1986, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: kernel_socket.c,v 1.9 2001/11/05 20:57:41 tfraser Exp $ + * + * This file implements LOMAC controls over socket operations. LOMAC + * gains control of socket operations by interposing on the `struct + * pr_usrreqs' operations vectors of each `struct protosw'. This code + * replaces each `struct pr_usrreqs' with an instance of `struct + * lomac_pr_usrreqs' containing LOMAC socket control functions. These + * socket control functions implement LOMAC's socket controls, and then + * call the corresponding socket operations from the original `struct + * pr_usrreqs'. Each instance of `struct lomac_pr_usrreqs' ends with + * a pointer to the `struct pr_usrreqs' it replaces. These pointers + * allow the LOMAC socket control functions to find their corresponding + * original `struct pr_usrreqs' functions. + * + * This file provides the function lomac_initialize_sockets() to turn + * socket interposition on. Once socket iterposition is turned on, + * the kernel will begin to call LOMAC's socket control functions. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> + +#include <sys/domain.h> +#include <sys/mbuf.h> +#include <sys/namei.h> +#include <sys/protosw.h> +#include <sys/socketvar.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/unpcb.h> +#include <sys/uio.h> +#include <sys/vnode.h> + +#include "kernel_interface.h" +#include "kernel_socket.h" +#include "kernel_mediate.h" +#include "kernel_monitor.h" +#include "lomacfs.h" + +MALLOC_DEFINE(M_LOMAC_USRREQS, "LOMAC-UR", "LOMAC usrreqs"); + +struct lomac_pr_usrreqs { + struct pr_usrreqs lomac_pr_usrreqs; /* LOMAC socket control fxns */ + struct pr_usrreqs *orig_pr_usrreqs; /* original socket op vector */ +}; + +int lomac_local_accept(struct socket *, struct sockaddr **); +int lomac_local_connect(struct socket *, struct sockaddr *, struct thread *); +int lomac_local_connect2(struct socket *, struct socket *); +int lomac_local_detach(struct socket *); +int lomac_local_send( struct socket *, int, struct mbuf *, struct sockaddr *, + struct mbuf *, struct thread * ); +int lomac_soreceive( struct socket *, struct sockaddr **, struct uio *, + struct mbuf **, struct mbuf **, int * ); +int lomac_local_soreceive( struct socket *, struct sockaddr **, struct uio *, + struct mbuf **, struct mbuf **, int * ); +static int monitored_soreceive( struct socket *, struct sockaddr **, + struct uio *, struct mbuf **, struct mbuf **, int * ); + +/* This usrreqs structure implements LOMAC's controls on local sockets */ +struct pr_usrreqs lomac_local_usrreqs = { + NULL, + lomac_local_accept, + NULL, + NULL, + lomac_local_connect, + lomac_local_connect2, + NULL, + lomac_local_detach, + NULL, + NULL, + NULL, + NULL, + NULL, + lomac_local_send, + NULL, + NULL, + NULL, + NULL, + lomac_local_soreceive, + NULL +}; + +/* This usrreqs structure implements LOMAC's controls on network sockets */ +struct pr_usrreqs lomac_net_usrreqs = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + lomac_soreceive, + NULL +}; + +static __inline struct pr_usrreqs * +orig_pr_usrreqs( struct socket *so ) { + return (((struct lomac_pr_usrreqs *)(so->so_proto->pr_usrreqs))-> + orig_pr_usrreqs); +} + +int +lomac_local_accept( struct socket *so, struct sockaddr **nam ) { + struct vnode *vp; + struct unpcb *unp; + int ret_val; /* value to return to caller */ + + unp = sotounpcb(so); + if (unp == NULL) + return (EINVAL); + if (unp->unp_conn != NULL) { + vp = unp->unp_vnode = unp->unp_conn->unp_vnode; + if (vp != NULL) + vref(vp); + } + ret_val = (*orig_pr_usrreqs(so)->pru_accept)(so, nam); + return (ret_val); +} + +int +lomac_local_connect(struct socket *so, struct sockaddr *nam, struct thread *td) +{ + register struct sockaddr_un *soun = (struct sockaddr_un *)nam; + register struct vnode *vp; + register struct socket *so2, *so3; + struct unpcb *unp, *unp2, *unp3; + int error, len; + struct nameidata nd; + char buf[SOCK_MAXADDRLEN]; + + len = nam->sa_len - offsetof(struct sockaddr_un, sun_path); + if (len <= 0) + return EINVAL; + strncpy(buf, soun->sun_path, len); + buf[len] = 0; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, buf, td); + error = namei(&nd); + if (error) + goto bad2; + vp = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + if (vp->v_type != VSOCK) { + error = ENOTSOCK; + goto bad; + } + error = VOP_ACCESS(vp, VWRITE, td->td_proc->p_ucred, td); + if (error) + goto bad; + so2 = vp->v_socket; + if (so2 == 0) { + error = ECONNREFUSED; + goto bad; + } + if (so->so_type != so2->so_type) { + error = EPROTOTYPE; + goto bad; + } + if (so->so_proto->pr_flags & PR_CONNREQUIRED) { + if ((so2->so_options & SO_ACCEPTCONN) == 0 || + (so3 = sonewconn3(so2, 0, td)) == 0) { + error = ECONNREFUSED; + goto bad; + } + unp = sotounpcb(so); + unp2 = sotounpcb(so2); + unp3 = sotounpcb(so3); + if (unp2->unp_addr) + unp3->unp_addr = (struct sockaddr_un *) + dup_sockaddr((struct sockaddr *) + unp2->unp_addr, 1); + + /* + * unp_peercred management: + * + * The connecter's (client's) credentials are copied + * from its process structure at the time of connect() + * (which is now). + */ + memset(&unp3->unp_peercred, '\0', sizeof(unp3->unp_peercred)); + unp3->unp_peercred.cr_uid = td->td_proc->p_ucred->cr_uid; + unp3->unp_peercred.cr_ngroups = td->td_proc->p_ucred->cr_ngroups; + memcpy(unp3->unp_peercred.cr_groups, td->td_proc->p_ucred->cr_groups, + sizeof(unp3->unp_peercred.cr_groups)); + unp3->unp_flags |= UNP_HAVEPC; + /* + * The receiver's (server's) credentials are copied + * from the unp_peercred member of socket on which the + * former called listen(); unp_listen() cached that + * process's credentials at that time so we can use + * them now. + */ + KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED, + ("unp_connect: listener without cached peercred")); + memcpy(&unp->unp_peercred, &unp2->unp_peercred, + sizeof(unp->unp_peercred)); + unp->unp_flags |= UNP_HAVEPC; + + so2 = so3; + } + error = lomac_local_connect2(so, so2); +bad: + vput(vp); +bad2: + return (error); +} + +int +lomac_local_connect2( struct socket *so1, struct socket *so2 ) { + struct vnode *vp; + int ret_val; /* value to return to caller */ + + if (so2->so_head != NULL) { + vp = sotounpcb(so2->so_head)->unp_vnode; + if (vp != NULL) { + sotounpcb(so1)->unp_vnode = vp; + vref(vp); + } + } + ret_val = (*orig_pr_usrreqs(so1)->pru_connect2)(so1, so2); + return (ret_val); +} + +int +lomac_local_detach( struct socket *so ) { + int ret_val; /* value to return to caller */ + struct unpcb *unp = sotounpcb(so); + + if (unp == NULL) + return (EINVAL); + if (unp->unp_vnode != NULL && unp->unp_vnode->v_socket != so) { + vrele(unp->unp_vnode); + unp->unp_vnode = NULL; + } + ret_val = (*orig_pr_usrreqs(so)->pru_detach)(so); + return (ret_val); +} + +int +lomac_local_send( struct socket *so, int flags, struct mbuf *m, + struct sockaddr *addr, struct mbuf *control, struct thread *td ) { + struct vnode *vp; + struct unpcb *unp = sotounpcb(so); + int error; + + /* printf( "pid %d local send\n", p->p_pid ); */ + if (unp == NULL) { + error = EINVAL; + goto out; + } + if (so->so_type == SOCK_DGRAM) { + if (addr != NULL) { + if (unp->unp_conn != NULL) { + error = EISCONN; + goto out; + } + error = lomac_local_connect(so, addr, td); + if (error) + goto out; + } else if (unp->unp_conn == NULL) { + error = ENOTCONN; + goto out; + } + } else if ((so->so_state & SS_ISCONNECTED) == 0) { + if (addr != NULL) { + error = lomac_local_connect(so, addr, td); + if (error) + goto out; /* XXX */ + } else { + error = ENOTCONN; + goto out; + } + } + vp = unp->unp_vnode; + if (vp != NULL) { + lomac_object_t lobj; + + lobj.lo_type = VISLOMAC(vp) ? LO_TYPE_LVNODE : LO_TYPE_UVNODE; + lobj.lo_object.vnode = vp; + if (!mediate_subject_object("send", td->td_proc, &lobj)) { + error = EPERM; + goto out; + } + } else { + /* + * This is a send to a socket in a socketpair() pair. + * Mark both sockets in pair with the appropriate level. + */ + lomac_object_t lobj1, lobj2; + lattr_t lattr; + + lobj1.lo_type = LO_TYPE_SOCKETPAIR; + lobj1.lo_object.socket = so; + if ((error = monitor_pipe_write(td->td_proc, &lobj1)) != 0) + goto out; + lobj2.lo_type = LO_TYPE_SOCKETPAIR; + lobj2.lo_object.socket = unp->unp_conn->unp_socket; + get_object_lattr(&lobj1, &lattr); + set_object_lattr(&lobj2, lattr); + } + error = (*orig_pr_usrreqs(so)->pru_send)( so, flags, m, NULL, + control, td ); + if (addr != NULL && so->so_type == SOCK_DGRAM) + (*orig_pr_usrreqs(so)->pru_disconnect)(so); +out: + return (error); +} + + + +int +lomac_local_soreceive(struct socket *so, struct sockaddr **paddr, + struct uio *uio, struct mbuf **mp0, struct mbuf **controlp, int *flagsp) { + lomac_object_t lobj; + struct vnode *vp; + struct unpcb *unp = sotounpcb(so); + int ret_val; /* value to return to caller */ + + if (unp == NULL) + return (EINVAL); + vp = unp->unp_vnode; + if (vp != NULL) { + lobj.lo_type = VISLOMAC(vp) ? LO_TYPE_LVNODE : LO_TYPE_UVNODE; + lobj.lo_object.vnode = vp; + ret_val = monitor_read_object(uio->uio_td->td_proc, &lobj); + if (ret_val == 0) + ret_val = (*orig_pr_usrreqs(so)->pru_soreceive)(so, + paddr, uio, mp0, controlp, flagsp); + } else { + /* + * This is a receive from a socket in a pair created by + * socketpair(). Monitor it as we would a pipe read, + * except for allowing for arbitrary numbers of sleeps. + */ + ret_val = monitored_soreceive(so, paddr, uio, mp0, controlp, + flagsp); + } + return (ret_val); +} + +int +lomac_soreceive(struct socket *so, struct sockaddr **paddr, struct uio *uio, + struct mbuf **mp0, struct mbuf **controlp, int *flagsp) { + int ret_val; /* value to return to caller */ + + (void)monitor_read_net_socket(uio->uio_td->td_proc); + ret_val = (*orig_pr_usrreqs(so)->pru_soreceive)(so, paddr, uio, mp0, + controlp, flagsp); + return (ret_val); +} + +int +lomac_initialize_sockets(void) { + struct domain *dp; /* used to traverse global `domains' list */ + struct protosw *pr; /* used to traverse each domain's protosw list */ + struct lomac_pr_usrreqs *lomac_pr_usrreqs; /* lomac usrreqs vectors */ + void (**lfuncp)(void), (**funcp)(void); + int n, nreq; + + nreq = sizeof(struct pr_usrreqs) / sizeof(void (*)(void)); + for (dp = domains; dp; dp = dp->dom_next) { + for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { + + lomac_pr_usrreqs = (struct lomac_pr_usrreqs *)malloc( + sizeof(struct lomac_pr_usrreqs), M_LOMAC_USRREQS, + M_WAITOK); + + if (dp->dom_family == AF_LOCAL) + memcpy(lomac_pr_usrreqs, &lomac_local_usrreqs, + sizeof(struct pr_usrreqs)); + else + memcpy(lomac_pr_usrreqs, &lomac_net_usrreqs, + sizeof(struct pr_usrreqs)); + /* + * Do sparse allocation of user requests and only + * override the ones we need to (to reduce overhead). + */ + lfuncp = (void (**)(void))lomac_pr_usrreqs; + funcp = (void (**)(void))pr->pr_usrreqs; + for (n = 0; n < nreq; n++) { + if (lfuncp[n] == NULL) + lfuncp[n] = funcp[n]; + } + lomac_pr_usrreqs->orig_pr_usrreqs = pr->pr_usrreqs; + pr->pr_usrreqs = (struct pr_usrreqs *)lomac_pr_usrreqs; + } + } + return (0); +} + + +int +lomac_uninitialize_sockets(void) { + struct domain *dp; /* used to traverse global `domains' list */ + struct protosw *pr; /* used to traverse each domain's protosw list */ + struct lomac_pr_usrreqs *lomac_pr_usrreqs; /* lomac usrreqs vectors */ + + for (dp = domains; dp; dp = dp->dom_next) { + for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { + lomac_pr_usrreqs = (struct lomac_pr_usrreqs *) + pr->pr_usrreqs; + pr->pr_usrreqs = lomac_pr_usrreqs->orig_pr_usrreqs; + free(lomac_pr_usrreqs, M_LOMAC_USRREQS); + } + } + return (0); +} + +#define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK) +/* + * Implement receive operations on a socket. + * We depend on the way that records are added to the sockbuf + * by sbappend*. In particular, each record (mbufs linked through m_next) + * must begin with an address if the protocol so specifies, + * followed by an optional mbuf or mbufs containing ancillary data, + * and then zero or more mbufs of data. + * In order to avoid blocking network interrupts for the entire time here, + * we splx() while doing the actual copy to user space. + * Although the sockbuf is locked, new data may still be appended, + * and thus we must maintain consistency of the sockbuf during that time. + * + * The caller may receive the data as a single mbuf chain by supplying + * an mbuf **mp0 for use in returning the chain. The uio is then used + * only for the count in uio_resid. + */ +static int +monitored_soreceive(so, psa, uio, mp0, controlp, flagsp) + register struct socket *so; + struct sockaddr **psa; + struct uio *uio; + struct mbuf **mp0; + struct mbuf **controlp; + int *flagsp; +{ + lomac_object_t lobj; + register struct mbuf *m, **mp; + register int flags, len, error, s, offset; + struct protosw *pr = so->so_proto; + struct mbuf *nextrecord; + struct proc *p; + int moff, type = 0; + int orig_resid = uio->uio_resid; + + mp = mp0; + if (psa) + *psa = 0; + if (controlp) + *controlp = 0; + if (flagsp) + flags = *flagsp &~ MSG_EOR; + else + flags = 0; + lobj.lo_type = LO_TYPE_SOCKETPAIR; + lobj.lo_object.socket = so; + if (uio->uio_td != NULL) /* XXX */ + p = uio->uio_td->td_proc; + else + p = curthread->td_proc; + if (flags & MSG_OOB) { + m = m_get(M_TRYWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + error = (*pr->pr_usrreqs->pru_rcvoob)(so, m, flags & MSG_PEEK); + if (error) + goto bad; + do { + monitor_read_object(p, &lobj); + error = uiomove(mtod(m, caddr_t), + (int) min(uio->uio_resid, m->m_len), uio); + m = m_free(m); + } while (uio->uio_resid && error == 0 && m); +bad: + if (m) + m_freem(m); + return (error); + } + if (mp) + *mp = (struct mbuf *)0; + if (so->so_state & SS_ISCONFIRMING && uio->uio_resid) + (*pr->pr_usrreqs->pru_rcvd)(so, 0); + +restart: + error = sblock(&so->so_rcv, SBLOCKWAIT(flags)); + if (error) + return (error); + s = splnet(); + + m = so->so_rcv.sb_mb; + /* + * If we have less data than requested, block awaiting more + * (subject to any timeout) if: + * 1. the current count is less than the low water mark, or + * 2. MSG_WAITALL is set, and it is possible to do the entire + * receive operation at once if we block (resid <= hiwat). + * 3. MSG_DONTWAIT is not set + * If MSG_WAITALL is set but resid is larger than the receive buffer, + * we have to do the receive in sections, and thus risk returning + * a short count if a timeout or signal occurs after we start. + */ + if (m == 0 || (((flags & MSG_DONTWAIT) == 0 && + so->so_rcv.sb_cc < uio->uio_resid) && + (so->so_rcv.sb_cc < so->so_rcv.sb_lowat || + ((flags & MSG_WAITALL) && uio->uio_resid <= so->so_rcv.sb_hiwat)) && + m->m_nextpkt == 0 && (pr->pr_flags & PR_ATOMIC) == 0)) { + KASSERT(m != 0 || !so->so_rcv.sb_cc, + ("receive: m == %p so->so_rcv.sb_cc == %lu", + m, so->so_rcv.sb_cc)); + if (so->so_error) { + if (m) + goto dontblock; + error = so->so_error; + if ((flags & MSG_PEEK) == 0) + so->so_error = 0; + goto release; + } + if (so->so_state & SS_CANTRCVMORE) { + if (m) + goto dontblock; + else + goto release; + } + for (; m; m = m->m_next) + if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) { + m = so->so_rcv.sb_mb; + goto dontblock; + } + if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 && + (so->so_proto->pr_flags & PR_CONNREQUIRED)) { + error = ENOTCONN; + goto release; + } + if (uio->uio_resid == 0) + goto release; + if ((so->so_state & SS_NBIO) || (flags & MSG_DONTWAIT)) { + error = EWOULDBLOCK; + goto release; + } + sbunlock(&so->so_rcv); + error = sbwait(&so->so_rcv); + splx(s); + if (error) + return (error); + goto restart; + } +dontblock: + if (uio->uio_td) + p->p_stats->p_ru.ru_msgrcv++; + nextrecord = m->m_nextpkt; + if (pr->pr_flags & PR_ADDR) { + KASSERT(m->m_type == MT_SONAME, ("receive 1a")); + orig_resid = 0; + if (psa) + *psa = dup_sockaddr(mtod(m, struct sockaddr *), + mp0 == 0); + if (flags & MSG_PEEK) { + m = m->m_next; + } else { + sbfree(&so->so_rcv, m); + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + } + while (m && m->m_type == MT_CONTROL && error == 0) { + if (flags & MSG_PEEK) { + if (controlp) + *controlp = m_copy(m, 0, m->m_len); + m = m->m_next; + } else { + sbfree(&so->so_rcv, m); + so->so_rcv.sb_mb = m->m_next; + m->m_next = NULL; + if (pr->pr_domain->dom_externalize) + error = + (*pr->pr_domain->dom_externalize)(m, controlp); + else if (controlp) + *controlp = m; + else + m_freem(m); + m = so->so_rcv.sb_mb; + } + if (controlp) { + orig_resid = 0; + do + controlp = &(*controlp)->m_next; + while (*controlp != NULL); + } + } + if (m) { + if ((flags & MSG_PEEK) == 0) + m->m_nextpkt = nextrecord; + type = m->m_type; + if (type == MT_OOBDATA) + flags |= MSG_OOB; + } + moff = 0; + offset = 0; + while (m && uio->uio_resid > 0 && error == 0) { + if (m->m_type == MT_OOBDATA) { + if (type != MT_OOBDATA) + break; + } else if (type == MT_OOBDATA) + break; + else + KASSERT(m->m_type == MT_DATA || m->m_type == MT_HEADER, + ("receive 3")); + so->so_state &= ~SS_RCVATMARK; + len = uio->uio_resid; + if (so->so_oobmark && len > so->so_oobmark - offset) + len = so->so_oobmark - offset; + if (len > m->m_len - moff) + len = m->m_len - moff; + /* + * If mp is set, just pass back the mbufs. + * Otherwise copy them out via the uio, then free. + * Sockbuf must be consistent here (points to current mbuf, + * it points to next record) when we drop priority; + * we must note any additions to the sockbuf when we + * block interrupts again. + */ + if (mp == 0) { + splx(s); + monitor_read_object(p, &lobj); + error = uiomove(mtod(m, caddr_t) + moff, (int)len, uio); + s = splnet(); + if (error) + goto release; + } else + uio->uio_resid -= len; + if (len == m->m_len - moff) { + if (m->m_flags & M_EOR) + flags |= MSG_EOR; + if (flags & MSG_PEEK) { + m = m->m_next; + moff = 0; + } else { + nextrecord = m->m_nextpkt; + sbfree(&so->so_rcv, m); + if (mp) { + *mp = m; + mp = &m->m_next; + so->so_rcv.sb_mb = m = m->m_next; + *mp = (struct mbuf *)0; + } else { + MFREE(m, so->so_rcv.sb_mb); + m = so->so_rcv.sb_mb; + } + if (m) + m->m_nextpkt = nextrecord; + } + } else { + if (flags & MSG_PEEK) + moff += len; + else { + if (mp) + *mp = m_copym(m, 0, len, M_TRYWAIT); + m->m_data += len; + m->m_len -= len; + so->so_rcv.sb_cc -= len; + } + } + if (so->so_oobmark) { + if ((flags & MSG_PEEK) == 0) { + so->so_oobmark -= len; + if (so->so_oobmark == 0) { + so->so_state |= SS_RCVATMARK; + break; + } + } else { + offset += len; + if (offset == so->so_oobmark) + break; + } + } + if (flags & MSG_EOR) + break; + /* + * If the MSG_WAITALL flag is set (for non-atomic socket), + * we must not quit until "uio->uio_resid == 0" or an error + * termination. If a signal/timeout occurs, return + * with a short count but without error. + * Keep sockbuf locked against other readers. + */ + while (flags & MSG_WAITALL && m == 0 && uio->uio_resid > 0 && + !sosendallatonce(so) && !nextrecord) { + if (so->so_error || so->so_state & SS_CANTRCVMORE) + break; + /* + * Notify the protocol that some data has been + * drained before blocking. + */ + if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) + (*pr->pr_usrreqs->pru_rcvd)(so, flags); + error = sbwait(&so->so_rcv); + if (error) { + sbunlock(&so->so_rcv); + splx(s); + return (0); + } + m = so->so_rcv.sb_mb; + if (m) + nextrecord = m->m_nextpkt; + } + } + + if (m && pr->pr_flags & PR_ATOMIC) { + flags |= MSG_TRUNC; + if ((flags & MSG_PEEK) == 0) + (void) sbdroprecord(&so->so_rcv); + } + if ((flags & MSG_PEEK) == 0) { + if (m == 0) + so->so_rcv.sb_mb = nextrecord; + if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) + (*pr->pr_usrreqs->pru_rcvd)(so, flags); + } + if (orig_resid == uio->uio_resid && orig_resid && + (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { + sbunlock(&so->so_rcv); + splx(s); + goto restart; + } + + if (flagsp) + *flagsp |= flags; +release: + sbunlock(&so->so_rcv); + splx(s); + return (error); +} diff --git a/sys/contrib/lomac/kernel_socket.h b/sys/contrib/lomac/kernel_socket.h new file mode 100644 index 0000000..4da513f --- /dev/null +++ b/sys/contrib/lomac/kernel_socket.h @@ -0,0 +1,35 @@ +/* + * LOMAC - Low Water-Mark Mandatory Access Control + * Copyright (c) 2001 Networks Associates Technology, Inc. + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: kernel_socket.h,v 1.1 2001/09/26 23:00:44 tfraser Exp $ + * + */ + +#ifndef _KERNEL_SOCKET_H_ +#define _KERNEL_SOCKET_H_ + +int lomac_initialize_sockets( void ); +int lomac_uninitialize_sockets( void ); + +#endif /* _KERNEL_SOCKET_H_ */ + diff --git a/sys/contrib/lomac/kernel_util.c b/sys/contrib/lomac/kernel_util.c new file mode 100644 index 0000000..3a46070 --- /dev/null +++ b/sys/contrib/lomac/kernel_util.c @@ -0,0 +1,687 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * Copyright (c) 1982, 1986, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_util.c,v 1.22 2001/11/15 20:51:13 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/signalvar.h> +#include <sys/sx.h> +#include <sys/syscall.h> +#include <sys/sysent.h> +#include <sys/sysproto.h> +#include <sys/systm.h> +#include <sys/linker.h> +#include <sys/mount.h> +#include <sys/mman.h> +#include <sys/dirent.h> +#include <sys/namei.h> +#include <sys/uio.h> +#include <sys/unistd.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> + +#include "kernel_interface.h" +#include "kernel_util.h" +#include "kernel_mediate.h" +#include "kernel_monitor.h" +#include "lomacfs.h" + +#include "syscall_gate/syscall_gate.h" + +#define AS(name) (sizeof(struct name) / sizeof(register_t)) + +int +each_proc(int (*iter)(struct proc *p)) { + struct proc *p; + int error = 0; + + sx_slock(&allproc_lock); + LIST_FOREACH(p, &allproc, p_list) { + error = (*iter)(p); + if (error) + goto out; + } +out: + sx_sunlock(&allproc_lock); + return (error); +} + +static int +initialize_proc(struct proc *p) { + lattr_t lattr = { LOMAC_HIGHEST_LEVEL, 0 }; + + init_subject_lattr(p, &lattr); + return (0); +} + +static void +lomac_at_fork(struct proc *parent, struct proc *p, int flags) { + + if ((flags & RFMEM) == 0) { + lattr_t parent_lattr; + + get_subject_lattr(parent, &parent_lattr); + init_subject_lattr(p, &parent_lattr); + } +} + +static int +lomac_proc_candebug(struct proc *p1, struct proc *p2) { + lattr_t lattr; + + get_subject_lattr(p1, &lattr); + if (mediate_subject_level_subject("debug", p1, lattr.level, p2)) + return (0); + else + return (EPERM); +} + +static int +lomac_proc_cansched(struct proc *p1, struct proc *p2) { + lattr_t lattr; + + get_subject_lattr(p1, &lattr); + if (mediate_subject_level_subject("sched", p1, lattr.level, p2)) + return (0); + else + return (EPERM); +} + +static int +lomac_proc_cansignal(struct proc *p1, struct proc *p2, int signum) { + lattr_t lattr; + + get_subject_lattr(p1, &lattr); + /* + * Always allow signals to init(8) (necessary to shut down). + */ + if (p2->p_pid == 1 || + mediate_subject_level_subject("signal", p1, lattr.level, p2)) + return (0); + else + return (EPERM); +} + + +int +lomac_initialize_procs(void) { + int error; + +#ifdef P_CAN_HOOKS + can_hooks_lock(); + (void)p_candebug_hook(lomac_proc_candebug); + (void)p_cansignal_hook(lomac_proc_cansignal); + (void)p_cansched_hook(lomac_proc_cansched); + can_hooks_unlock(); +#endif + error = at_fork(lomac_at_fork); + if (error) + return (error); + return (each_proc(&initialize_proc)); +} + +int +lomac_uninitialize_procs(void) { + + rm_at_fork(lomac_at_fork); + return (0); +} + +extern int (*old_execve)(struct thread *, struct execve_args *); + +int +execve(struct thread *td, struct execve_args *uap) { + lattr_t lattr, textattr; + struct vmspace *oldvmspace; + struct proc *p; + int error; + + p = td->td_proc; + get_subject_lattr(p, &lattr); + oldvmspace = p->p_vmspace; + error = old_execve(td, uap); + if (error == 0) { + lomac_object_t lobj; + + lobj.lo_type = VISLOMAC(p->p_textvp) ? LO_TYPE_LVNODE : + LO_TYPE_UVNODE; + lobj.lo_object.vnode = p->p_textvp; + get_object_lattr(&lobj, &textattr); + /* + * Install the executable's relevant attributes into the + * process. + */ + lattr.flags |= textattr.flags & + (LOMAC_ATTR_NODEMOTE | LOMAC_ATTR_NONETDEMOTE); + if (p->p_vmspace != oldvmspace) + init_subject_lattr(p, &lattr); + else + set_subject_lattr(p, lattr); + mtx_lock(&Giant); + (void)monitor_read_object(p, &lobj); + mtx_unlock(&Giant); + } + return (error); +} + +const char *linker_basename(const char* path); +int linker_load_module(const char *kldname, const char *modname, + struct linker_file *parent, struct mod_depend *verinfo, + struct linker_file **lfpp); + +MALLOC_DECLARE(M_LINKER); + +/* + * MPSAFE + */ +int +kldload(struct thread* td, struct kldload_args* uap) +{ + char *kldname, *modname; + char *pathname = NULL; + linker_file_t lf; + int error = 0; + + td->td_retval[0] = -1; + + if (securelevel > 0) /* redundant, but that's OK */ + return EPERM; + + mtx_lock(&Giant); + + if ((error = suser_td(td)) != 0) + goto out; + + pathname = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + if ((error = copyinstr(SCARG(uap, file), pathname, MAXPATHLEN, NULL)) != 0) + goto out; + if (!mediate_subject_at_level("kldload", td->td_proc, + LOMAC_HIGHEST_LEVEL)) { + error = EPERM; + goto out; + } + + /* + * If path do not contain qualified name or any dot in it (kldname.ko, or + * kldname.ver.ko) treat it as interface name. + */ + if (index(pathname, '/') || index(pathname, '.')) { + kldname = pathname; + modname = NULL; + } else { + kldname = NULL; + modname = pathname; + } + error = linker_load_module(kldname, modname, NULL, NULL, &lf); + if (error) + goto out; + + lf->userrefs++; + td->td_retval[0] = lf->id; + +out: + if (pathname) + free(pathname, M_TEMP); + mtx_unlock(&Giant); + return (error); +} + + +#ifdef __i386__ +#include <machine/sysarch.h> + +extern int (*old_sysarch)(struct thread *, void *); + +int +sysarch(struct thread *td, struct sysarch_args *uap) { + switch (uap->op) { + case I386_SET_IOPERM: + if (!mediate_subject_at_level("ioperm", td->td_proc, + LOMAC_HIGHEST_LEVEL)) + return (EPERM); + default: + return (old_sysarch(td, uap)); + } +} +#endif + +extern int lomac_mmap(struct proc *, struct mmap_args *); + +/* + * Mount a file system. + */ +#ifndef _SYS_SYSPROTO_H_ +struct mount_args { + char *type; + char *path; + int flags; + caddr_t data; +}; +#endif +/* ARGSUSED */ +int +mount(td, uap) + struct thread *td; + struct mount_args /* { + syscallarg(char *) type; + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(caddr_t) data; + } */ *uap; +{ + char *fstype; + char *fspath; + int error; + + fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK | M_ZERO); + fspath = malloc(MNAMELEN, M_TEMP, M_WAITOK | M_ZERO); + + /* + * vfs_mount() actually takes a kernel string for `type' and + * `path' now, so extract them. + */ + error = copyinstr(SCARG(uap, type), fstype, MFSNAMELEN, NULL); + if (error) + goto finish; + error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); + if (error) + goto finish; + if (!mediate_subject_at_level("mount", td->td_proc, + LOMAC_HIGHEST_LEVEL)) { + error = EPERM; + goto finish; + } + error = vfs_mount(td, fstype, fspath, SCARG(uap, flags), + SCARG(uap, data)); +finish: + free(fstype, M_TEMP); + free(fspath, M_TEMP); + return (error); +} + +/* + * Unmount a file system. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). + */ +#ifndef _SYS_SYSPROTO_H_ +struct unmount_args { + char *path; + int flags; +}; +#endif +/* ARGSUSED */ +int +unmount(td, uap) + struct thread *td; + register struct unmount_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap; +{ + register struct vnode *vp; + struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), td); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + NDFREE(&nd, NDF_ONLY_PNBUF); + mp = vp->v_mount; + + /* + * Only root, or the user that did the original mount is + * permitted to unmount this filesystem. + */ + if (!mediate_subject_at_level("unmount", td->td_proc, + LOMAC_HIGHEST_LEVEL) || + ((mp->mnt_stat.f_owner != td->td_proc->p_ucred->cr_uid) && + (error = suser_td(td)))) { + vput(vp); + return (error); + } + + /* + * Don't allow unmounting the root file system. + */ + if (mp->mnt_flag & MNT_ROOTFS) { + vput(vp); + return (EINVAL); + } + + /* + * Must be the root of the filesystem + */ + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + vput(vp); + return (dounmount(mp, SCARG(uap, flags), td)); +} + +static struct syscall_override { + int offset; + sy_call_t *call; + int narg; + int mpsafe; +} syscall_overrides[] = { + { SYS_mmap, (sy_call_t *)mmap, AS(mmap_args), 1 }, + { SYS_execve, (sy_call_t *)execve, AS(execve_args), 1 }, + { SYS_kldload, (sy_call_t *)kldload, AS(kldload_args), 1 }, + { SYS_mount, (sy_call_t *)mount, AS(mount_args), 0 }, + { SYS_unmount, (sy_call_t *)unmount, AS(unmount_args), 0 }, +#ifdef __i386__ + { SYS_sysarch, (sy_call_t *)sysarch, AS(sysarch_args), 1 } +#endif +}; + +int +lomac_initialize_syscalls(void) { + int error, i; + + for (i = 0; + i < sizeof(syscall_overrides) / sizeof(syscall_overrides[0]); i++) { + struct syscall_override *so = &syscall_overrides[i]; + + error = syscall_gate_register(so->offset, so->call, so->narg, + so->mpsafe); + if (error) { + while (--i >= 0) + syscall_gate_deregister( + syscall_overrides[i].offset); + return (error); + } + } + return (0); +} + +int +lomac_uninitialize_syscalls(void) { + int i; + + for (i = 0; + i < sizeof(syscall_overrides) / sizeof(syscall_overrides[0]); i++) + syscall_gate_deregister(syscall_overrides[i].offset); + return (0); +} + +/* This memory is shared by all lomac_do_recwd() calls, in sequence. */ +static char *pathmem; +#define DIRENTMEM_SIZE (64 << 10) /* 64KB is good, I guess! */ +static char *direntmem; + +static int +lomac_dirents_searchbyid(struct vnode *dvp, struct dirent *dp, + struct dirent *enddp, const struct vattr *vap, struct dirent **retdp) +{ + struct vattr pvattr; + struct componentname cnp; + struct thread *td = curthread; + struct ucred *ucred = td->td_ucred; + struct vnode *vp; + int error; + + *retdp = NULL; + for (; dp != enddp; dp = (struct dirent *)((char *)dp + dp->d_reclen)) { + cnp.cn_nameiop = LOOKUP; + cnp.cn_flags = LOCKPARENT | ISLASTCN | NOFOLLOW; + cnp.cn_thread = td; + cnp.cn_cred = ucred; + cnp.cn_nameptr = dp->d_name; + cnp.cn_namelen = dp->d_namlen; + + error = VOP_LOOKUP(dvp, &vp, &cnp); + if (error) + return (error); + error = VOP_GETATTR(vp, &pvattr, ucred, td); + if (vp != dvp) + (void)vput(vp); + else + vrele(vp); /* if looking up "." */ + if (error) + return (error); + if (pvattr.va_fsid == vap->va_fsid && + pvattr.va_fileid == vap->va_fileid) { + *retdp = dp; + break; + } + } + return (0); +} + +static int +lomac_getcwd( + struct thread *td, + char *buf, + size_t buflen, + char **bufret +) { + struct vattr cvattr; + char *bp; + int error, i, slash_prefixed; + struct filedesc *fdp; + struct vnode *vp, *startvp, *dvp; + + if (buflen < 2) + return (EINVAL); + if (buflen > MAXPATHLEN) + buflen = MAXPATHLEN; + bp = buf; + bp += buflen - 1; + *bp = '\0'; + fdp = td->td_proc->p_fd; + slash_prefixed = 0; +#if defined(LOMAC_DEBUG_RECWD) + printf("lomac_getcwd for %d:\n", td->td_proc->p_pid); +#endif + startvp = fdp->fd_cdir; + vref(startvp); + for (vp = startvp; vp != rootvnode; vp = dvp) { + struct iovec diov = { + direntmem, + DIRENTMEM_SIZE + }; + struct uio duio = { + &diov, + 1, + 0, + DIRENTMEM_SIZE, + UIO_SYSSPACE, + UIO_READ, + curthread + }; + struct dirent *dp; + int direof; + + if (vp->v_flag & VROOT) { + if (vp->v_mount == NULL) /* forced unmount */ + return (EBADF); + dvp = vp->v_mount->mnt_vnodecovered; + continue; + } + dvp = vp->v_dd; + if (vp == dvp) + break; + /* + * Utilize POSIX requirement of files having same + * st_dev and st_ino to be the same file, in our + * case with vattr.va_fsid and vattr.va_fileid. + */ + error = vget(vp, LK_EXCLUSIVE, curthread); + if (error) + goto out2; + error = VOP_GETATTR(vp, &cvattr, curthread->td_ucred, + curthread); + if (error) + goto out2; + (void)vput(vp); + error = vget(dvp, LK_EXCLUSIVE, curthread); + if (error) + goto out2; + for (direof = 0; !direof;) { + error = VOP_READDIR(dvp, &duio, + curthread->td_ucred, &direof, NULL, NULL); + if (error) + break; + error = lomac_dirents_searchbyid(dvp, + (struct dirent *)direntmem, + (struct dirent *)(direntmem + + DIRENTMEM_SIZE - duio.uio_resid), + &cvattr, + &dp); + if (error) + break; + if (dp != NULL) { + (void)vput(dvp); +#if defined(LOMAC_DEBUG_RECWD) + printf("\tdirent component: \"%.*s\"\n", + dp->d_namlen, dp->d_name); +#endif + for (i = dp->d_namlen - 1; i >= 0; i--) + if (bp == buf) + return (ENOMEM); + else + *--bp = dp->d_name[i]; + goto nextcomp; + } + diov.iov_base = direntmem; + diov.iov_len = DIRENTMEM_SIZE; + duio.uio_resid = DIRENTMEM_SIZE; + } + if (direof) + error = ENOENT; + (void)vput(dvp); + out2: +#if defined(LOMAC_DEBUG_RECWD) + printf("backup dirent lookup problem: %d\n", error); +#endif + goto out; + nextcomp: + if (bp == buf) + return (ENOMEM); + *--bp = '/'; + slash_prefixed = 1; + } + if (!slash_prefixed) { + if (bp == buf) + return (ENOMEM); + *--bp = '/'; + } + error = 0; + *bufret = bp; +out: + vrele(startvp); + return (error); +} + +static int +lomac_do_recwd(struct proc *p) { + struct nameidata nd; + struct filedesc *fdp = curthread->td_proc->p_fd; + struct thread *td = &p->p_thread; + char *nbuf; + struct vnode *cdir, *rdir, *vp; + int error; + + if (p == curthread->td_proc) + return (0); + PROC_LOCK(p); + if (p->p_flag & P_SYSTEM) { + PROC_UNLOCK(p); + return (0); + } + PROC_UNLOCK(p); + error = lomac_getcwd(td, pathmem, MAXPATHLEN, &nbuf); + if (error) { +#if defined(LOMAC_DEBUG_RECWD) + printf("lomac: recwd() failure, lomac_getcwd() == %d\n", + error); +#endif + return (0); + } + rdir = fdp->fd_rdir; + fdp->fd_rdir = rootvnode; + vref(fdp->fd_rdir); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, + nbuf, curthread); + error = namei(&nd); + vrele(fdp->fd_rdir); + fdp->fd_rdir = rdir; + if (error == 0) { + vp = nd.ni_vp; + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, td->td_proc->p_ucred, + curthread); + if (error) + vput(vp); + else { + NDFREE(&nd, NDF_ONLY_PNBUF); + fdp = p->p_fd; + cdir = fdp->fd_cdir; + fdp->fd_cdir = vp; + vrele(cdir); + VOP_UNLOCK(vp, 0, curthread); + } + } +#if defined(LOMAC_DEBUG_RECWD) + printf("\trecwd() to \"%.*s\" == %d\n", + MAXPATHLEN, nbuf, error); +#endif + return (0); +} + +int +lomac_initialize_cwds(void) { + int error; + + pathmem = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); + direntmem = malloc(DIRENTMEM_SIZE, M_TEMP, M_WAITOK); + mtx_lock(&Giant); + error = each_proc(lomac_do_recwd); + mtx_unlock(&Giant); + free(pathmem, M_TEMP); + free(direntmem, M_TEMP); + return (error); +} diff --git a/sys/contrib/lomac/kernel_util.h b/sys/contrib/lomac/kernel_util.h new file mode 100644 index 0000000..46a9ec9 --- /dev/null +++ b/sys/contrib/lomac/kernel_util.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kernel_util.h,v 1.4 2001/11/14 16:30:17 bfeldman Exp $ + */ +#ifndef KERNEL_UTIL_H +#define KERNEL_UTIL_H + +/* + * Iterate through each proc, locking it and calling iter. + * Short-circuit and return an error if the iterator ever returns one. + */ +int each_proc(int (*iter)(struct proc *p)); + +/* + * Set the initial level on each proc, register at_fork(). + */ +int lomac_initialize_procs(void); + +/* + * Unregister at_fork(). + */ +int lomac_uninitialize_procs(void); + +int lomac_initialize_cwds(void); + +int lomac_initialize_syscalls(void); +int lomac_uninitialize_syscalls(void); + +int lomac_initialize_vm(void); +int lomac_uninitialize_vm(void); +#endif /* KERNEL_UTIL_H */ diff --git a/sys/contrib/lomac/lomac.h b/sys/contrib/lomac/lomac.h new file mode 100644 index 0000000..b16941a --- /dev/null +++ b/sys/contrib/lomac/lomac.h @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacfs.h,v 1.20 2001/10/17 15:34:29 bfeldman Exp $ + */ + +#ifndef _LOMAC_H_ +#define _LOMAC_H_ + +/* + * This file defines the `lattr_t' type, which represents + * the architecture-independent notion of LOMAC attributes. + * + * Each architecture must associate LOMAC attributes with subjects and + * objects. This association can be implemented in an architecture- + * specific way. However, when it comes time to make a decision by + * comparing two LOMAC attributes, the architecture-specific code should + * construct two instances of the architecture-independent lattr_t type + * and compare them using the lomac_must_demote() and lomac_must_deny() + * functions. + * + * The following two examples demonstrate how architecture-specific code + * might do this construction and comparison: + * + * EXAMPLE USAGE: + * + * Example 1: subject x reads object y. + * (1) a = LOMAC attributes of subject x + * (2) b = LOMAC attributes of object y + * (3) demote_result = lomac_must_demote( a, b ); + * (4) IF demote_result THEN + * (5) IF subject x is running in "deny read instead of demote" mode THEN + * (6) RETURN read denied + * (7) ENDIF + * (8) IF subject x is not running in "never demote" mode THEN + * (9) demote subject x + * (10) ENDIF + * (11) ENDIF + * (12) perform read on object y + * + * + * Example 2: subject x writes object y. + * (50) a = LOMAC attributes of subject x + * (51) b = LOMAC attributes of object y + * (52) IF lomac_must_deny( a, b ) THEN + * (53) return write denied + * (54) ELSE + * (55) perform write operation on object y + * (56) ENDIF + * + * Lines 1, 2, 50, and 51 show the architecture-specific code + * constructing instances of lattr_t. + * + * Lines 5 and 8 ask "is the subject running in some mode?" (See note + * on modes, below.) The architecture-specific code must use these + * modes to determine when to call lomac_must_demote/deny() and when not + * to. + * + * Lines 6, 9, 53 and 55 show the architecture-specific code + * taking different actions depending on the results of calls to + * lomac_must_demote/deny(). The architecture-specific code is responsible + * for calling lomac_must_demote/deny() in the proper places, and carrying + * out the appropriate demotions and denials depending on the result. + * + * + * A NOTE ON LEVELS: + * + * LOMAC presently supports only two levels: 1 and 2. Future versions + * of LOMAC may support more levels. Architecture-specific code may + * assume that the LOWEST and HIGHEST constants defined below will + * always refer to the lowest and highest levels in the range. They + * may also provide support for only two levels for the time being. + * However, architecture-specific code should try to minimize any other + * assumptions about levels, in order to make it easier to increase + * the level range in the future. + * + * + * A NOTE ON CATEGORIES: + * + * The lattr_t structure's `flags' field is intended to be a bitfield + * which architecture-specific code can use to implement categories. + * The lomac_must_deny() function interprets the bits in the flags field + * as categories. A clear flags field means no categories. + * + * A NOTE ON MODES: + * + * LOMAC allows subjects to run in many modes, such as "never demote" + * or "no demote on IPC reads". Support for these modes is entirely + * the responsibility of the architecture-specific code, because the + * architecture-independent code doesn't know about operations like + * "read" or "read on an IPC object". + * + *************************************************************************/ + +typedef enum { + LOMAC_LOWEST_LEVEL = 1, + LOMAC_HIGHEST_LEVEL = 2 +} level_t; + + +typedef struct { + level_t level; /* level (an integer range) */ + unsigned int flags; /* category flags */ +} lattr_t; /* lomac attribute structure type */ + + +/* lomac_must_demote() + * + * in: actor - attributes of a subject that has or will perform an + * operation that may require LOMAC to demote it. + * target - attributes of the object that is or was the operand. + * out: nothing + * return: value condition + * ----- --------- + * 0 LOMAC should not demote the subject + * 1 LOMAC should demote the subject + * + * This function is a predicate which decides whether or not LOMAC should + * demote the subject with attributes `actor' after it performs an operation + * (probably some kind of a read operation) on the object with attributes + * `target'. + * + */ + +static __inline int lomac_must_demote( const lattr_t *actor, + const lattr_t *target ) { + return( ( actor->level > target->level ) ); +} + + +/* lomac_must_deny() + * + * in: actor - attributes of a subject that wants to perform some + * operation that requires LOMAC to make an allow/deny + * decision. + * target - attributes of the subject or object the above subject + * will operate upon. + * out: nothing + * return: value condition + * ----- --------- + * 0 LOMAC should allow the operation + * 1 LOMAC should deny the operation + * + * This function is a predicate which decides whether or not LOMAC should + * allow the subject with attributes `actor' to perform some operation + * (probably some kind of write or kill operation) on the subject or object + * with attributes `target'. + * + * The flags are two words: the low word is to be used for categories, + * and the high word is meant to hold implementation-dependent flags that + * are not category-related. + * + */ + +static __inline int lomac_must_deny( const lattr_t *actor, + const lattr_t *target ) { + + if( actor->level >= target->level ) { + return 0; /* allow */ + } + if( target->flags & 0xffff ) { + if( ( actor->flags & target->flags & 0xffff ) == + ( target->flags & 0xffff ) ) { + return 0; /* allow */ + } + } + + return 1; /* deny */ +} + +#endif /* lomac.h */ diff --git a/sys/contrib/lomac/lomacfs.h b/sys/contrib/lomac/lomacfs.h new file mode 100644 index 0000000..621761a --- /dev/null +++ b/sys/contrib/lomac/lomacfs.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacfs.h,v 1.20 2001/10/17 15:34:29 bfeldman Exp $ + */ + +#ifndef LOMACFS_H +#define LOMACFS_H + +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/vnode.h> +#include <sys/mount.h> + +#include "kernel_interface.h" + +struct lomac_mount { + struct vnode *lm_rootvp; /* singly-ref'd root after mount() */ +#define LM_TOOKROOT 0x0001 + unsigned int lm_flags; +}; + +/* + * This is the structure associated with v_data on all LOMACFS vnodes. + */ +struct lomac_node { + struct vnode *ln_vp; /* vnode back-pointer */ + struct vnode *ln_lowervp; /* shadowed vnode (ref'd or NULL) */ +#define LN_LEVEL_MASK 0x0003 +#define LN_LOWEST_LEVEL 0x0001 +#define LN_SUBJ_LEVEL 0x0002 /* placeholder before inheriting */ +#define LN_HIGHEST_LEVEL 0x0003 +#define LN_INHERIT_MASK 0x001c +#define LN_INHERIT_LOW 0x0004 /* children start with a low level */ +#define LN_INHERIT_HIGH 0x0008 /* children start with a high level */ +#define LN_INHERIT_SUBJ 0x0010 /* children inherit subject's level */ +#define LN_ATTR_MASK 0x01e0 +#define LN_ATTR_LOWWRITE 0x0020 /* lower levels may write to */ +#define LN_ATTR_LOWNOOPEN 0x0040 /* lower levels may not open */ +#define LN_ATTR_NONETDEMOTE 0x0080 /* will not demote on net read */ +#define LN_ATTR_NODEMOTE 0x0100 /* subject won't demote on other read */ + u_int ln_flags; + /* What's the last node explicitly specifying policy for this? */ + struct lomac_node_entry *ln_underpolicy; + /* If non-NULL, this corresponds 1:1 to a specific PLM node entry. */ + struct lomac_node_entry *ln_entry; +#if defined(LOMAC_DEBUG_INCNAME) + char ln_name[MAXPATHLEN]; /* final component name */ +#endif +}; + +/* + * This is the "placeholder" structure initialized from the PLM that + * holds the level information for all named objects. + */ +struct lomac_node_entry { + SLIST_HEAD(lomac_node_entry_head, lomac_node_entry) ln_children; + SLIST_ENTRY(lomac_node_entry) ln_chain; /* chain of current level */ + /* continuing with the LN_* flags above */ +#define LN_CHILD_ATTR_SHIFT 4 /* lshift from attr -> child attr */ +#define LN_CHILD_ATTR_MASK 0x1e00 +#define LN_CHILD_ATTR_LOWWRITE 0x0200 /* lower levels may write to */ +#define LN_CHILD_ATTR_LOWNOOPEN 0x0400 /* lower levels may not open */ +#define LN_CHILD_ATTR_NONETDEMOTE 0x0800 /* will not demote on net read */ +#define LN_CHILD_ATTR_NODEMOTE 0x1000 /* subject won't demote on other read */ + u_int ln_flags; + char *ln_name; /* last component name (to search) */ + const char *ln_path; /* in "stable" storage */ +}; + +#define VTOLOMAC(vp) ((struct lomac_node *)(vp)->v_data) +#define VTOLVP(vp) VTOLOMAC(vp)->ln_lowervp +#define VFSTOLOMAC(mp) ((struct lomac_mount *)mp->mnt_data) +#define VISLOMAC(vp) (vp->v_op == lomacfs_vnodeop_p) + +int lomacfs_node_alloc(struct mount *mp, struct componentname *cnp, + struct vnode *dvp, struct vnode *lowervp, struct vnode **vpp); + +MALLOC_DECLARE(M_LOMACFS); +extern vop_t **lomacfs_vnodeop_p; +extern struct lomac_node_entry lomac_node_entry_root; + +#endif /* LOMACFS_H */ diff --git a/sys/contrib/lomac/lomacfs_subr.c b/sys/contrib/lomac/lomacfs_subr.c new file mode 100644 index 0000000..b58abc1 --- /dev/null +++ b/sys/contrib/lomac/lomacfs_subr.c @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacfs_subr.c,v 1.24 2001/11/05 20:57:41 tfraser Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/namei.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> + +#include "lomacfs.h" +#include "kernel_plm.h" + +int +lomacfs_node_alloc(struct mount *mp, struct componentname *cnp, + struct vnode *dvp, struct vnode *lowervp, struct vnode **vpp) { + lomac_object_t lobj; + struct thread *td = curthread; + struct vnode *vp; + struct lomac_node *lp; + lattr_t subjlattr, objlattr; + int error; + + KASSERT((cnp == NULL) == (dvp == NULL), + ("lomacfs_node_alloc: dvp and cnp do not match")); + lp = malloc(sizeof(*lp), M_LOMACFS, M_WAITOK); + if (dvp != NULL) { + error = cache_lookup(dvp, vpp, cnp); + if (error == -1) { /* lost the race; return EEXIST and the vp */ + vput(lowervp); + error = vget(*vpp, LK_EXCLUSIVE, td); + free(lp, M_LOMACFS); + if (error) { + *vpp = NULL; + return (error); + } else + return (EEXIST); + } + } + error = getnewvnode(VT_NULL, mp, lomacfs_vnodeop_p, vpp); + if (error) { + vput(lowervp); + free(lp, M_LOMACFS); + return (error); + } + vp = *vpp; + + vp->v_type = lowervp != NULL ? lowervp->v_type : VBAD; + if (vp->v_type == VCHR) + vp->v_rdev = lowervp->v_rdev; + vp->v_data = lp; + lp->ln_vp = vp; + lp->ln_lowervp = lowervp; + if (lowervp != NULL) + vhold(lowervp); + get_subject_lattr(curthread->td_proc, &subjlattr); + lp->ln_flags = 0; + lomac_plm_init_lomacfs_vnode(dvp, vp, cnp, &subjlattr); + /* retrieve the just-initialized attributes */ + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = vp; + get_object_lattr(&lobj, &objlattr); + /* propogate the lattr to the underlying vnode */ + lobj.lo_type = LO_TYPE_UVNODE; + lobj.lo_object.vnode = lowervp; + set_object_lattr(&lobj, objlattr); +#if defined(LOMAC_DEBUG_INCNAME) + if (cnp == NULL) + strncpy(lp->ln_name, "/", sizeof(lp->ln_name)); + else { + strncpy(lp->ln_name, cnp->cn_nameptr, cnp->cn_namelen); + lp->ln_name[cnp->cn_namelen] = '\0'; + } +#endif + error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_THISLAYER, td); + if (error) + panic("lomacfs_node_alloc: can't lock new vnode\n"); + if (cnp == NULL) + vp->v_flag |= VROOT; + else if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, vp, cnp); + +#if defined(LOMAC_DEBUG_NODE_ALLOC) + printf("lomacfs: made vp %p for lvp %p \"%.*s\" in dvp %p from %s\n", + vp, lowervp, cnp ? (int)cnp->cn_namelen : 0, + cnp ? cnp->cn_nameptr : "", dvp, + lowervp != NULL ? lowervp->v_mount->mnt_stat.f_mntonname : ""); +#endif + + return (0); +} diff --git a/sys/contrib/lomac/lomacfs_vfsops.c b/sys/contrib/lomac/lomacfs_vfsops.c new file mode 100644 index 0000000..381ad2e --- /dev/null +++ b/sys/contrib/lomac/lomacfs_vfsops.c @@ -0,0 +1,190 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacfs_vfsops.c,v 1.16 2001/10/17 19:36:39 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/vnode.h> + +#include "lomacfs.h" +#include "kernel_mediate.h" + +MALLOC_DEFINE(M_LOMACFS, "LOMACFS", "LOMAC filesystem objects"); + +static int lomacfs_mount(struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct thread *td); +static int lomacfs_statfs(struct mount *mp, struct statfs *sbp, + struct thread *td); +static int lomacfs_unmount(struct mount *mp, int mntflags, struct thread *td); + +static int +lomacfs_mount(struct mount *mp, char *path, caddr_t data, + struct nameidata *ndp, struct thread *td) { + + if (mp->mnt_flag & MNT_UPDATE || VISLOMAC(mp->mnt_vnodecovered)) + return (EOPNOTSUPP); + + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_flag &= ~MNT_RDONLY; + mp->mnt_data = malloc(sizeof(struct lomac_mount), M_LOMACFS, + M_WAITOK | M_ZERO); + vfs_getnewfsid(mp); + + strncpy(mp->mnt_stat.f_mntfromname, "lomacfs", MNAMELEN); + (void)lomacfs_statfs(mp, &mp->mnt_stat, td); + + /* + * Keep around an extra ref for dounmount() to vrele() after the + * VFS_UNMOUNT()... (who knows...) + */ + vref(mp->mnt_vnodecovered); + if (VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) + VOP_UNLOCK(mp->mnt_vnodecovered, 0, td); + + return (0); +} + +static int +lomacfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) { + struct statfs tmpstat; + int error; + + bzero(&tmpstat, sizeof(tmpstat)); + + error = VFS_STATFS(mp->mnt_vnodecovered->v_mount, &tmpstat, td); + if (error) + return (error); + + sbp->f_type = tmpstat.f_type; + sbp->f_flags = tmpstat.f_flags; + sbp->f_bsize = tmpstat.f_bsize; + sbp->f_iosize = tmpstat.f_iosize; + sbp->f_blocks = tmpstat.f_blocks; + sbp->f_bfree = tmpstat.f_bfree; + sbp->f_bavail = tmpstat.f_bavail; + sbp->f_files = tmpstat.f_files; + sbp->f_ffree = tmpstat.f_ffree; + if (sbp != &mp->mnt_stat) { + bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); + bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); + bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); + } + return (0); +} + +static int +lomacfs_unmount(struct mount *mp, int mntflags, struct thread *td) { + struct vnode *crootvp = VFSTOLOMAC(mp)->lm_rootvp; + int error; + int flags = 0; + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + + if (VFSTOLOMAC(mp)->lm_flags & LM_TOOKROOT) { + mtx_lock(&crootvp->v_interlock); + crootvp->v_flag |= VROOT; + mtx_unlock(&crootvp->v_interlock); + } + + error = vflush(mp, 1, flags); /* have an extra root ref */ + if (error) + return (error); + + free(VFSTOLOMAC(mp), M_LOMACFS); + + /* bye, lomacfs. */ + return (0); +} + +static int +lomacfs_root(struct mount *mp, struct vnode **vpp) { + int error; + + if (VFSTOLOMAC(mp)->lm_rootvp == NULL) { + struct vnode *rootvp, *crootvp; + + crootvp = mp->mnt_vnodecovered; + error = lomacfs_node_alloc(mp, NULL, NULL, crootvp, &rootvp); + if (error) + return (error); + /* + * Reference twice to match the rest of the lomacfs vnodes. + */ + vref(crootvp); + vref(crootvp); + VFSTOLOMAC(mp)->lm_rootvp = rootvp; + /* + * This releases the lock on root, but it doesn't release + * the reference so that root won't "disappear" until + * unmount. + */ + error = VOP_UNLOCK(rootvp, 0, curthread); + if (error) + return (error); + /* + * This is some strange magic here... I need to pretend + * that the mounted-on directory isn't a root vnode if I + * want things like __getcwd() to just fail and not crash. + */ + mtx_lock(&crootvp->v_interlock); + if (crootvp->v_flag & VROOT && crootvp == rootvnode) { + crootvp->v_flag &= ~VROOT; + VFSTOLOMAC(mp)->lm_flags |= LM_TOOKROOT; + } + mtx_unlock(&crootvp->v_interlock); + } + *vpp = VFSTOLOMAC(mp)->lm_rootvp; + return (vget(*vpp, LK_EXCLUSIVE, curthread)); +} + +static struct vfsops lomacfs_vfsops = { + lomacfs_mount, + vfs_stdstart, + lomacfs_unmount, + lomacfs_root, + vfs_stdquotactl, + lomacfs_statfs, + vfs_stdsync, + vfs_stdvget, + vfs_stdfhtovp, + vfs_stdcheckexp, + vfs_stdvptofh, + vfs_stdinit, + vfs_stduninit, + vfs_stdextattrctl +}; +VFS_SET(lomacfs_vfsops, lomacfs, VFCF_LOOPBACK); +MODULE_VERSION(lomacfs, 1); +MODULE_DEPEND(lomacfs, lomac_plm, 1, 1, 1); diff --git a/sys/contrib/lomac/lomacfs_vnops.c b/sys/contrib/lomac/lomacfs_vnops.c new file mode 100644 index 0000000..35c0c84 --- /dev/null +++ b/sys/contrib/lomac/lomacfs_vnops.c @@ -0,0 +1,1141 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacfs_vnops.c,v 1.44 2001/11/15 20:52:54 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_object.h> +#include <vm/vnode_pager.h> + +#include <machine/limits.h> + +#include "lomacfs.h" +#include "kernel_mediate.h" +#include "kernel_monitor.h" + +#if defined(LOMAC_DEBUG_LOOKUPSTATS) +static unsigned int lomacfs_successful_lookups, lomacfs_failed_lookups, + lomacfs_successful_cachedlookups, lomacfs_failed_cachedlookups, + lomacfs_node_alloc_clashes, lomacfs_node_alloc_failures; + +SYSCTL_NODE(_vfs, OID_AUTO, lomacfs, CTLFLAG_RW, 0, "LOMACFS filesystem"); +SYSCTL_NODE(_vfs_lomacfs, OID_AUTO, debug, CTLFLAG_RW, 0, "debug stats"); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, successful_lookups, + CTLFLAG_RW, &lomacfs_successful_lookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, failed_lookups, + CTLFLAG_RW, &lomacfs_failed_lookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, successful_cachedlookups, + CTLFLAG_RW, &lomacfs_successful_cachedlookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, failed_cachedlookups, + CTLFLAG_RW, &lomacfs_failed_cachedlookups, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, node_alloc_clashes, + CTLFLAG_RW, &lomacfs_node_alloc_clashes, 0, ""); +SYSCTL_UINT(_vfs_lomacfs_debug, OID_AUTO, node_alloc_failures, + CTLFLAG_RW, &lomacfs_node_alloc_failures, 0, ""); +#endif + +static int +lomacfs_defaultop( + struct vop_generic_args /* { + struct vnodeop_desc *a_desc; + } */ *ap +) { + + printf("lomacfs: %s unsupported\n", ap->a_desc->vdesc_name); + return (EOPNOTSUPP); +} + +static int +lomacfs_inactive( + struct vop_inactive_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VTOLVP(vp); + struct thread *td = ap->a_td; + + KASSERT(lvp != NULL, ("inactive with NULL lowervp")); + VOP_UNLOCK(ap->a_vp, 0, td); + /* + * Temporarily drop our reference to the lower vnode, while keeping + * it held, to possibly call VOP_INACTIVE() on the lower layer. + */ + vrele(lvp); +#if defined(LOMAC_DEBUG_INACTIVE) + do { +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = VTOLOMAC(vp)->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: inactive(%p \"%s\"), lvp usecount down to %u\n", + vp, name, lvp->v_usecount); + } while (0); +#endif + /* + * Since the lower fs may actually remove the vnode on last + * release, destroy ourselves mostly here if that occurs. + * + * Additionally, devices should be totally freed + * on last close, not lazily. + */ + if (lvp->v_usecount == 0 && + (lvp->v_type != VREG && lvp->v_type != VDIR)) { + vdrop(lvp); + VTOLVP(vp) = NULL; + cache_purge(vp); + } else + vref(lvp); + return (0); +} + +static int +lomacfs_reclaim( + struct vop_reclaim_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct lomac_node *ln = VTOLOMAC(vp); + struct vnode *lvp = VTOLVP(vp); + + if (lvp != NULL) + vrele(lvp); +#if defined(LOMAC_DEBUG_RECLAIM) + if (lvp != NULL) { +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = ln->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: reclaim(%p \"%s\"), lvp usecount down to %u\n", + vp, name, lvp->v_usecount); + } +#endif + if (lvp != NULL) + vdrop(lvp); + vp->v_data = NULL; + vp->v_rdev = NULL; + free(ln, M_LOMACFS); + + return (0); +} + +static int +lomacfs_print( + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + + printf ("\ttag VT_LOMACFS, vp=%p, lowervp=%p\n", vp, + VTOLVP(vp)); + return (0); +} + +static int +lomacfs_lock( + struct vop_lock_args /* { + struct vnode *a_vp; + int a_flags; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + int flags = ap->a_flags; + struct thread *td = ap->a_td; + struct vnode *lvp; + int lflags = flags & ~(LK_INTERLOCK | LK_THISLAYER); + int error; + + /* + * To prevent race conditions involving doing a lookup + * on "..", we have to lock the lower node, then lock our + * node. Most of the time it won't matter that we lock our + * node (as any locking would need the lower one locked + * first). But we can LK_DRAIN the upper lock as a step + * towards decomissioning it. + */ + lvp = VTOLVP(vp); + if (lvp == NULL || flags & LK_THISLAYER) + return (lockmgr(&vp->v_lock, flags, &vp->v_interlock, td)); + if (flags & LK_INTERLOCK) { + mtx_unlock(&vp->v_interlock); + flags &= ~LK_INTERLOCK; + } + if ((flags & LK_TYPE_MASK) == LK_DRAIN) { + error = vn_lock(lvp, + (lflags & ~LK_TYPE_MASK) | LK_EXCLUSIVE | LK_CANRECURSE, + td); + } else + error = vn_lock(lvp, lflags | LK_CANRECURSE, td); + if (error) + return (error); + error = lockmgr(&vp->v_lock, flags, &vp->v_interlock, td); + if (error) + VOP_UNLOCK(lvp, 0, td); + return (error); +} + +/* + * We need to process our own vnode unlock and then clear the + * interlock flag as it applies only to our vnode, not the + * vnodes below us on the stack. + */ +static int +lomacfs_unlock( + struct vop_unlock_args /* { + struct vnode *a_vp; + int a_flags; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + int flags = ap->a_flags; + int lflags = (ap->a_flags | LK_RELEASE) & + ~(LK_THISLAYER | LK_INTERLOCK); + struct thread *td = ap->a_td; + struct vnode *lvp = VTOLVP(vp); + int error; + + error = lockmgr(&vp->v_lock, flags | LK_RELEASE, &vp->v_interlock, td); + if (lvp == NULL || flags & LK_THISLAYER || error) + return (error); + /* + * Hmm... in a vput(), this means we'll grab the lomacfs interlock, + * then the lower interlock. I don't think this matters, though, + * since both won't be held at the same time. + */ + if (lvp != NULL) + error = VOP_UNLOCK(lvp, lflags, td); + return (error); +} + +static int +lomacfs_islocked( + struct vop_islocked_args /* { + struct vnode *a_vp; + struct thread *a_td; + } */ *ap +) { + + struct vnode *vp = ap->a_vp; + struct thread *td = ap->a_td; + + return (lockstatus(&vp->v_lock, td)); +} + +static int +lomacfs_lookup( + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap +) { + int error; + + error = vfs_cache_lookup(ap); +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error == 0) + lomacfs_successful_lookups++; + else + lomacfs_failed_lookups++; +#endif +#if defined(LOMAC_DEBUG_LOOKUP) + if (error == 0 && (*ap->a_vpp)->v_mount == dvp->v_mount) { + struct vnode *vp = *ap->a_vpp; +#if defined(LOMAC_DEBUG_INCNAME) + const char *name = VTOLOMAC(vp)->ln_name; +#else + const char *name = "[unknown]"; +#endif + printf("lomacfs: lookup(%p \"%s\"), lvp usecount up to %u\n", + vp, name, VTOLVP(vp)->v_usecount); + } +#endif + return (error); +} + +static int +lomacfs_cachedlookup( + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct vnode *ldvp = VTOLVP(dvp); + struct vnode *lvp; + int makeentry; + int error; + + if (cnp->cn_flags & ISLASTCN && cnp->cn_nameiop != LOOKUP && + cnp->cn_nameiop != CREATE) { + lomac_object_t lobj = { LO_TYPE_LVNODE, { dvp } }; + const char *op; + + if (cnp->cn_nameiop == DELETE) + op = "delete"; + else + op = "rename"; + + if (!mediate_subject_object(op, curthread->td_proc, &lobj)) + return (EPERM); + } + makeentry = cnp->cn_flags & MAKEENTRY; + cnp->cn_flags &= ~makeentry; + error = VOP_LOOKUP(ldvp, &lvp, cnp); + cnp->cn_flags |= makeentry; + if ((error == 0 || error == EJUSTRETURN) && + cnp->cn_flags != (cnp->cn_flags | LOCKPARENT | ISLASTCN)) + (void)VOP_UNLOCK(dvp, LK_THISLAYER, curthread); + if (error == 0 && lvp->v_type != VSOCK) { + struct mount *mp; + + /* + * Check to see if the vnode has been mounted on; + * if so find the root of the mounted file system. + */ + if (lvp->v_type == VDIR && (mp = lvp->v_mountedhere) && + (cnp->cn_flags & NOCROSSMOUNT) == 0) { + struct vnode *tdp; + + if (vfs_busy(mp, 0, 0, curthread)) + goto forget_it; + VOP_UNLOCK(lvp, 0, curthread); + error = VFS_ROOT(mp, &tdp); + vfs_unbusy(mp, curthread); + if (error) { + vrele(lvp); + return (error); + } + vrele(lvp); + lvp = tdp; + } +forget_it: + /* + * For a create or for devices (dynamic things, aren't they), + * don't enter the vnode into the cache. + */ + if (cnp->cn_nameiop == CREATE || lvp->v_type == VCHR) + cnp->cn_flags &= ~makeentry; + /* + * The top half of dvp is locked, but ldvp is unlocked. + * Additionally, lvp is locked already, and + * lomacfs_node_alloc() always returns it locked. + */ + error = lomacfs_node_alloc(dvp->v_mount, cnp, + dvp, lvp, ap->a_vpp); + if (cnp->cn_nameiop == CREATE) + cnp->cn_flags |= makeentry; +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error) { + if (error != EEXIST) { + lomacfs_node_alloc_failures++; + } else { + lomacfs_node_alloc_clashes++; + error = 0; + } + } +#else + if (error == EEXIST) + error = 0; +#endif + } else if (error == 0) { + /* + * For sockets, just return the "real" thing + * after entering it into the cache. + */ + *ap->a_vpp = lvp; + if (cnp->cn_nameiop != CREATE && cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, lvp, cnp); + } + +#if defined(LOMAC_DEBUG_LOOKUPSTATS) + if (error == 0) + lomacfs_successful_cachedlookups++; + else + lomacfs_failed_cachedlookups++; +#endif + return (error); +} + +static int +lomacfs_getattr( + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct thread *a_td; + */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + int error; + + error = VOP_GETATTR(VTOLVP(vp), vap, ap->a_cred, ap->a_td); + if (error == 0 && vap->va_fsid == VNOVAL) + vap->va_fsid = VTOLVP(vp)->v_mount->mnt_stat.f_fsid.val[0]; + return (error); +} + +static int +lomacfs_setattr( + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct thread *a_td; + */ *ap +) { + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + if (mediate_subject_object(ap->a_desc->vdesc_name, curthread->td_proc, + &lobj)) + error = VOP_SETATTR(VTOLVP(ap->a_vp), ap->a_vap, ap->a_cred, + ap->a_td); + else + error = EPERM; + return (error); +} + +static int +lomacfs_readdir( + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + int *a_eofflag; + int *a_ncookies; + u_long **a_cookies; + } */ *ap +) { + + return (VOP_READDIR(VTOLVP(ap->a_vp), ap->a_uio, ap->a_cred, + ap->a_eofflag, ap->a_ncookies, ap->a_cookies)); +} + +static int +lomacfs_open( + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + int error; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object_open(ap->a_td->td_proc, &lobj)) + error = EPERM; + else + error = VOP_OPEN(VTOLVP(ap->a_vp), ap->a_mode, ap->a_cred, + ap->a_td); + return (error); +} + +static int +lomacfs_close( + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VTOLVP(vp); + int error; + + /* + * XXX + * Try to cope with the horrible semantics introduced here... + */ + vref(lvp); + error = VOP_CLOSE(lvp, ap->a_fflag, ap->a_cred, ap->a_td); + if (error == EAGAIN) + error = 0; + else + vrele(lvp); + return (error); +} + +static int +lomacfs_access( + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_ACCESS(VTOLVP(ap->a_vp), ap->a_mode, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_readlink( + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + if (lvp == NULL) + return (EPERM); + return (VOP_READLINK(lvp, ap->a_uio, ap->a_cred)); +} + +static int +lomacfs_lease( + struct vop_lease_args /* { + struct vnode *a_vp; + struct thread *a_td; + struct ucred *a_cred; + int a_flag; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + return (VOP_LEASE(lvp, ap->a_td, ap->a_cred, ap->a_flag)); +} + +static int +lomacfs_read( + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + error = monitor_read_object(curthread->td_proc, &lobj); + if (error == 0) + error = VOP_READ(lvp, ap->a_uio, ap->a_ioflag, ap->a_cred); + return (error); +} + +static int +lomacfs_write( + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + lomac_object_t lobj = { LO_TYPE_LVNODE, { ap->a_vp } }; + int error; + + if (mediate_subject_object(ap->a_desc->vdesc_name, curthread->td_proc, + &lobj)) + error = VOP_WRITE(lvp, ap->a_uio, ap->a_ioflag, ap->a_cred); + else + error = EPERM; + return (error); +} + +static int +lomacfs_ioctl( + struct vop_ioctl_args /* { + struct vnode *a_vp; + u_long a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + return (VOP_IOCTL(lvp, ap->a_command, ap->a_data, ap->a_fflag, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_muxcreate( + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct vnode *ldvp = VTOLVP(dvp); + struct componentname *cnp = ap->a_cnp; + struct vattr *vap = ap->a_vap; + int makeentry = cnp->cn_flags & MAKEENTRY; + lomac_object_t lobj = { LO_TYPE_LVNODE, { dvp } }; + struct thread *td = curthread; + int error; + + if (!mediate_subject_object(ap->a_desc->vdesc_name, td->td_proc, + &lobj) || (vap->va_type == VCHR && + !mediate_subject_at_level("mknod", curthread->td_proc, + LOMAC_HIGHEST_LEVEL))) + return (EPERM); + ap->a_dvp = ldvp; + cnp->cn_flags &= ~makeentry; + error = VCALL(ldvp, ap->a_desc->vdesc_offset, ap); + if (error == 0) { + struct vnode *vp; + int issock; + + issock = vap->va_type == VSOCK; + vp = *ap->a_vpp; + *ap->a_vpp = NULL; + if (!issock) + cnp->cn_flags |= makeentry; + error = lomacfs_node_alloc(dvp->v_mount, cnp, dvp, vp, + ap->a_vpp); + if (error) + vput(vp); + else if (issock) { + /* + * I should really find a nicer way to do this. + */ + vref(vp); + vput(*ap->a_vpp); + *ap->a_vpp = vp; + (void)VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY, td); + } + } + return (error); +} + +static int +lomacfs_muxremove( + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + int error; + + ap->a_dvp = VTOLVP(dvp); + if (VISLOMAC(vp)) + ap->a_vp = VTOLVP(vp); + error = VCALL(ap->a_dvp, ap->a_desc->vdesc_offset, ap); + if (error == 0) + cache_purge(vp); + return (error); +} + +static int +lomacfs_fsync( + struct vop_fsync_args /* { + struct vnode *a_vp; + struct ucred *a_cred; + int a_waitfor; + struct thread *a_td; + } */ *ap +) { + + return (VOP_FSYNC(VTOLVP(ap->a_vp), ap->a_cred, ap->a_waitfor, + ap->a_td)); +} + +static int +lomacfs_advlock( + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap +) { + + return (VOP_ADVLOCK(VTOLVP(ap->a_vp), ap->a_id, ap->a_op, ap->a_fl, + ap->a_flags)); +} + +static int +lomacfs_whiteout( + struct vop_whiteout_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + int a_flags; + } */ *ap +) { + + return (VOP_WHITEOUT(VTOLVP(ap->a_dvp), ap->a_cnp, ap->a_flags)); +} + +static int +lomacfs_poll( + struct vop_poll_args /* { + struct vnode *a_vp; + int a_events; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_POLL(VTOLVP(ap->a_vp), ap->a_events, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_revoke( + struct vop_revoke_args /* { + struct vnode *a_vp; + int a_flags; + } */ *ap +) { + + return (VOP_REVOKE(VTOLVP(ap->a_vp), ap->a_flags)); +} + +static int +lomacfs_link( + struct vop_link_args /* { + struct vnode *a_tdvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap +) { + struct vnode *tdvp = ap->a_tdvp; + struct vnode *vp = ap->a_vp; + struct vnode *lvp = VISLOMAC(vp) ? VTOLVP(vp) : vp; + struct componentname *cnp = ap->a_cnp; + int error; + + error = VOP_LINK(VTOLVP(tdvp), lvp, cnp); + if (error == 0 && vp->v_type == VSOCK) { + cache_enter(tdvp, vp, cnp); +#if defined(LOMAC_DEBUG_LINK) + do { + struct vnode *nvp; + int nerror; + + nerror = cache_lookup(tdvp, &nvp, cnp); + printf("lomacfs: link(%p), cache_lookup() = %d (%p)\n", + vp, nerror, nvp); + } while (0); +#endif + } + return (error); +} + +static int +lomacfs_rename( + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap +) { + struct vnode *fdvp = ap->a_fdvp; + struct vnode *fvp = ap->a_fvp; + struct componentname *fcnp = ap->a_fcnp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *tvp = ap->a_tvp; + struct componentname *tcnp = ap->a_tcnp; + int fvp_is_lomac = VISLOMAC(fvp); + int error; + + vref(VTOLVP(fdvp)); + /* + * Handle the case when LOMAC returns a real vnode for + * VSOCK, rather than the LOMAC covering vnode. + */ + if (fvp_is_lomac) + vref(VTOLVP(fvp)); + vref(VTOLVP(tdvp)); + if (tvp != NULL) + vref(VTOLVP(tvp)); + error = VOP_RENAME(VTOLVP(fdvp), fvp_is_lomac ? VTOLVP(fvp) : fvp, fcnp, + VTOLVP(tdvp), tvp != NULL ? VTOLVP(tvp) : NULL, tcnp); + if (fvp->v_type == VDIR) { + if (tvp != NULL && tvp->v_type == VDIR) + cache_purge(tdvp); + cache_purge(fdvp); + } + cache_purge(fvp); + if (tvp != NULL) + cache_purge(tvp); + (void)VOP_UNLOCK(tdvp, LK_THISLAYER, curthread); + vrele(fdvp); + if (fvp_is_lomac) + vrele(fvp); + vrele(tdvp); + if (tvp != NULL) { + (void)VOP_UNLOCK(tvp, LK_THISLAYER, curthread); + vrele(tvp); + } else if (tcnp->cn_nameiop == RENAME /* NOCACHE unsets MAKEENTRY */ + && fvp->v_type == VSOCK) + cache_enter(tdvp, fvp, tcnp); + return (error); +} + +static int +lomacfs_strategy( + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap +) { + + return (VOP_STRATEGY(VTOLVP(ap->a_vp), ap->a_bp)); +} + +/* + * Let an underlying filesystem do the work of creating the "actual" + * vm_object_t, and we will reference it. + */ +static int +lomacfs_createvobject( + struct vop_createvobject_args /* { + struct vnode *vp; + struct ucred *cred; + struct proc *p; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + struct vnode *lowervp = VTOLOMAC(vp) != NULL ? VTOLVP(vp) : NULL; + int error; + + if (vp->v_type == VNON || lowervp == NULL) + return (EINVAL); + error = VOP_CREATEVOBJECT(lowervp, ap->a_cred, ap->a_td); + if (error) + return (error); + vp->v_flag |= VOBJBUF; + return (error); +} + +/* + * We need to destroy the lower vnode object only if we created it. + * XXX - I am very unsure about all of this. + */ +static int +lomacfs_destroyvobject( + struct vop_destroyvobject_args /* { + struct vnode *vp; + } */ *ap +) { + struct vnode *vp = ap->a_vp; + + vp->v_flag &= ~VOBJBUF; + return (0); +} + +static int +lomacfs_bmap( + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + int *a_runb; + } */ *ap +) { + + return (VOP_BMAP(VTOLVP(ap->a_vp), ap->a_bn, ap->a_vpp, ap->a_bnp, + ap->a_runp, ap->a_runb)); +} + +static int +lomacfs_getpages( + struct vop_getpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_reqpage; + vm_ooffset_t a_offset; + } */ *ap +) { + + return (VOP_GETPAGES(VTOLVP(ap->a_vp), ap->a_m, ap->a_count, + ap->a_reqpage, ap->a_offset)); +} + +static int +lomacfs_putpages( + struct vop_putpages_args /* { + struct vnode *a_vp; + vm_page_t *a_m; + int a_count; + int a_sync; + int *a_rtvals; + vm_ooffset_t a_offset; + } */ *ap +) { + + return (VOP_PUTPAGES(VTOLVP(ap->a_vp), ap->a_m, ap->a_count, + ap->a_sync, ap->a_rtvals, ap->a_offset)); +} + +static int +lomacfs_getvobject( + struct vop_getvobject_args /* { + struct vnode *a_vp; + struct vm_object **a_objpp; + } */ *ap +) { + struct vnode *lvp = VTOLVP(ap->a_vp); + + if (lvp == NULL) + return EINVAL; + return (VOP_GETVOBJECT(lvp, ap->a_objpp)); +} + +static int +lomacfs_kqfilter( + struct vop_kqfilter_args /* { + struct vnode *a_vp; + struct knote *a_kn; + } */ *ap +) { + + return (VOP_KQFILTER(VTOLVP(ap->a_vp), ap->a_kn)); +} + +static int +lomacfs_pathconf( + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap +) { + + return (VOP_PATHCONF(VTOLVP(ap->a_vp), ap->a_name, ap->a_retval)); +} + +static int +lomacfs_reallocblks( + struct vop_reallocblks_args /* { + struct vnode *a_vp; + struct cluster_save *a_buflist; + } */ *ap +) { + + return (VOP_REALLOCBLKS(VTOLVP(ap->a_vp), ap->a_buflist)); +} + +static int +lomacfs_freeblks( + struct vop_freeblks_args /* { + struct vnode *a_vp; + daddr_t a_addr; + daddr_t a_length; + } */ *ap +) { + + return (VOP_FREEBLKS(VTOLVP(ap->a_vp), ap->a_addr, ap->a_length)); +} + +static int +lomacfs_getacl( + struct vop_getacl_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_GETACL(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, ap->a_cred, + ap->a_td)); +} + +static int +lomacfs_setacl( + struct vop_setacl_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object("setacl", ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_SETACL(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_aclcheck( + struct vop_aclcheck_args /* { + struct vnode *a_vp; + acl_type_t a_type; + struct acl *a_aclp; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + + return (VOP_ACLCHECK(VTOLVP(ap->a_vp), ap->a_type, ap->a_aclp, + ap->a_cred, ap->a_td)); +} + +static int +lomacfs_getextattr( + struct vop_getextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (monitor_read_object(ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_GETEXTATTR(VTOLVP(ap->a_vp), ap->a_attrnamespace, + ap->a_name, ap->a_uio, ap->a_cred, ap->a_td)); +} + +static int +lomacfs_setextattr( + struct vop_setextattr_args /* { + struct vnode *a_vp; + int a_attrnamespace; + const char *a_name; + struct uio *a_uio; + struct ucred *a_cred; + struct thread *a_td; + } */ *ap +) { + lomac_object_t lobj; + + lobj.lo_type = LO_TYPE_LVNODE; + lobj.lo_object.vnode = ap->a_vp; + if (!mediate_subject_object("setextattr", ap->a_td->td_proc, &lobj)) + return (EPERM); + else + return (VOP_SETEXTATTR(VTOLVP(ap->a_vp), ap->a_attrnamespace, + ap->a_name, ap->a_uio, ap->a_cred, ap->a_td)); +} + +vop_t **lomacfs_vnodeop_p; +static struct vnodeopv_entry_desc lomacfs_vnodeop_entries[] = { + { &vop_default_desc, (vop_t *)lomacfs_defaultop }, + { &vop_inactive_desc, (vop_t *)lomacfs_inactive }, + { &vop_reclaim_desc, (vop_t *)lomacfs_reclaim }, + { &vop_print_desc, (vop_t *)lomacfs_print }, + { &vop_lock_desc, (vop_t *)lomacfs_lock }, + { &vop_unlock_desc, (vop_t *)lomacfs_unlock }, + { &vop_islocked_desc, (vop_t *)lomacfs_islocked }, + { &vop_lookup_desc, (vop_t *)lomacfs_lookup }, + { &vop_setattr_desc, (vop_t *)lomacfs_setattr }, + { &vop_getattr_desc, (vop_t *)lomacfs_getattr }, + { &vop_readdir_desc, (vop_t *)lomacfs_readdir }, + { &vop_open_desc, (vop_t *)lomacfs_open }, + { &vop_close_desc, (vop_t *)lomacfs_close }, + { &vop_access_desc, (vop_t *)lomacfs_access }, + { &vop_readlink_desc, (vop_t *)lomacfs_readlink }, + { &vop_lease_desc, (vop_t *)lomacfs_lease }, + { &vop_read_desc, (vop_t *)lomacfs_read }, + { &vop_write_desc, (vop_t *)lomacfs_write }, + { &vop_ioctl_desc, (vop_t *)lomacfs_ioctl }, + { &vop_create_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_mkdir_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_mknod_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_symlink_desc, (vop_t *)lomacfs_muxcreate }, + { &vop_remove_desc, (vop_t *)lomacfs_muxremove }, + { &vop_rmdir_desc, (vop_t *)lomacfs_muxremove }, + { &vop_fsync_desc, (vop_t *)lomacfs_fsync }, + { &vop_advlock_desc, (vop_t *)lomacfs_advlock }, + { &vop_whiteout_desc, (vop_t *)lomacfs_whiteout }, + { &vop_poll_desc, (vop_t *)lomacfs_poll }, + { &vop_link_desc, (vop_t *)lomacfs_link }, + { &vop_rename_desc, (vop_t *)lomacfs_rename }, + { &vop_revoke_desc, (vop_t *)lomacfs_revoke }, + { &vop_cachedlookup_desc, (vop_t *)lomacfs_cachedlookup }, + { &vop_lookup_desc, (vop_t *)lomacfs_lookup }, + { &vop_bmap_desc, (vop_t *)lomacfs_bmap }, + { &vop_getpages_desc, (vop_t *)lomacfs_getpages }, + { &vop_putpages_desc, (vop_t *)lomacfs_putpages }, + { &vop_strategy_desc, (vop_t *)lomacfs_strategy }, + { &vop_createvobject_desc, (vop_t *)lomacfs_createvobject }, + { &vop_destroyvobject_desc, (vop_t *)lomacfs_destroyvobject }, + { &vop_getvobject_desc, (vop_t *)lomacfs_getvobject }, + { &vop_getwritemount_desc, (vop_t *)vop_stdgetwritemount }, + { &vop_kqfilter_desc, (vop_t *)lomacfs_kqfilter }, + { &vop_pathconf_desc, (vop_t *)lomacfs_pathconf }, + { &vop_reallocblks_desc, (vop_t *)lomacfs_reallocblks }, + { &vop_freeblks_desc, (vop_t *)lomacfs_freeblks }, + { &vop_getacl_desc, (vop_t *)lomacfs_getacl }, + { &vop_setacl_desc, (vop_t *)lomacfs_setacl }, + { &vop_aclcheck_desc, (vop_t *)lomacfs_aclcheck }, + { &vop_getextattr_desc, (vop_t *)lomacfs_getextattr }, + { &vop_setextattr_desc, (vop_t *)lomacfs_setextattr }, + { NULL, NULL } +}; +static struct vnodeopv_desc lomacfs_vnodeopv_opv_desc = + { &lomacfs_vnodeop_p, lomacfs_vnodeop_entries }; +VNODEOP_SET(lomacfs_vnodeopv_opv_desc); diff --git a/sys/contrib/lomac/lomacio.h b/sys/contrib/lomac/lomacio.h new file mode 100644 index 0000000..d8ddc25 --- /dev/null +++ b/sys/contrib/lomac/lomacio.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: lomacio.h,v 1.4 2001/09/26 21:18:00 bfeldman Exp $ + */ +#ifndef LOMACIO_H +#define LOMACIO_H + +#include <sys/ioccom.h> +#include <sys/param.h> + +struct lomac_fioctl { + char path[MAXPATHLEN]; + int level; /* LOMAC security level */ +}; + +struct lomac_fioctl2 { + char path[MAXPATHLEN]; + int level; /* LOMAC security level */ + int flags; +}; + +#define LIOGETPLEVEL _IOWR('L', 0, int) /* get process level */ +#define LIOGETFLEVEL _IOWR('L', 1, struct lomac_fioctl) /* get file level */ +#define LIOGETFLATTR _IOWR('L', 3, struct lomac_fioctl2) /* get file level */ +#define LIOPMAKELOWLEVEL _IO('L', 2) /* lower proc's level */ +#endif /* LOMACIO_H */ diff --git a/sys/contrib/lomac/policy_plm.h b/sys/contrib/lomac/policy_plm.h new file mode 100644 index 0000000..abbd0ad --- /dev/null +++ b/sys/contrib/lomac/policy_plm.h @@ -0,0 +1,110 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: policy_plm.h,v 1.20 2001/11/15 20:55:05 bfeldman Exp $ + */ + +#ifndef LOMAC_PLM_H +#define LOMAC_PLM_H + +enum plm_level { + LOW, + SUBJ, + HIGH +}; +enum plm_flags { + PLM_NOFLAGS, /* rule applies to this node and its children */ + PLM_CHILDOF /* rule applies to node's children, not the node */ +}; +#define LOWWRITE LN_ATTR_LOWWRITE +#define LOWNOOPEN LN_ATTR_LOWNOOPEN +#define NONETDEMOTE LN_ATTR_NONETDEMOTE +#define NODEMOTE LN_ATTR_NODEMOTE + +static u_int plm_levelflags_to_node_flags[3][2] = { + { LN_LOWEST_LEVEL, LN_INHERIT_LOW }, + { LN_SUBJ_LEVEL, LN_INHERIT_SUBJ }, + { LN_HIGHEST_LEVEL, LN_INHERIT_HIGH } +}; + +typedef struct plm_rule { + enum plm_level level; /* LOMAC level */ + enum plm_flags flags; /* flags for PLM evaluation */ + unsigned int attr; /* LN_ATTR_MASK of flags */ + const char *path; /* absolute path for this PLM rule */ +} plm_rule_t; + +/* The `plm' array maps levels onto all of the files in the filesystem */ +static plm_rule_t plm[] = { + { HIGH, PLM_NOFLAGS, 0, "/" }, /* everything initially inherits high level */ + { HIGH, PLM_CHILDOF, 0, "/" }, + { HIGH, PLM_NOFLAGS, NONETDEMOTE, "/sbin/dhclient" }, + { HIGH, PLM_CHILDOF, 0, "/var" }, + { HIGH, PLM_CHILDOF, LOWWRITE, "/dev" }, + { HIGH, PLM_NOFLAGS, LOWNOOPEN, "/dev/mdctl" }, + { HIGH, PLM_NOFLAGS, LOWNOOPEN, "/dev/pci" }, + { HIGH, PLM_NOFLAGS, LOWNOOPEN, "/dev/kmem" }, + { HIGH, PLM_NOFLAGS, LOWNOOPEN, "/dev/mem" }, + { HIGH, PLM_NOFLAGS, LOWNOOPEN, "/dev/io" }, + { HIGH, PLM_CHILDOF, 0, "/etc" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/tmp" }, + { SUBJ, PLM_CHILDOF, 0, "/tmp" }, + { HIGH, PLM_NOFLAGS, 0, "/tmp/.X11-unix" }, + { HIGH, PLM_CHILDOF, LOWWRITE, "/tmp/.X11-unix" }, + { SUBJ, PLM_CHILDOF, 0, "/proc" }, + { LOW, PLM_CHILDOF, 0, "/mnt" }, /* all nfs mounts are low */ + { LOW, PLM_CHILDOF, 0, "/home" }, + { HIGH, PLM_NOFLAGS, NONETDEMOTE, "/usr/bin/env-nonetdemote" }, + { HIGH, PLM_NOFLAGS, NODEMOTE, "/usr/bin/env-nodemote" }, + { LOW, PLM_CHILDOF, 0, "/usr/home" }, + { LOW, PLM_CHILDOF, 0, "/var/lib" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/var/tmp" }, + { SUBJ, PLM_CHILDOF, 0, "/var/tmp" }, + { LOW, PLM_NOFLAGS, 0, "/var/tmp/vi.recover" }, + { SUBJ, PLM_CHILDOF, 0, "/var/tmp/vi.recover" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/usr/tmp" }, + { SUBJ, PLM_CHILDOF, 0, "/usr/tmp" }, + { HIGH, PLM_NOFLAGS, 0, "/usr/tmp/.X11-unix" }, + { HIGH, PLM_CHILDOF, LOWWRITE, "/usr/tmp/.X11-unix" }, + { LOW, PLM_NOFLAGS, 0, "/var/mail" }, + { LOW, PLM_CHILDOF, 0, "/var/mail" }, + { LOW, PLM_NOFLAGS, 0, "/var/spool/mqueue" }, + { LOW, PLM_CHILDOF, 0, "/var/spool/mqueue" }, + { LOW, PLM_NOFLAGS, 0, "/dev/log" }, + { HIGH, PLM_NOFLAGS, 0, "/home/ftp" }, + { HIGH, PLM_NOFLAGS, 0, "/usr/home/ftp" }, + { HIGH, PLM_NOFLAGS, 0, "/mnt/cdrom" }, /* cdrom is high */ + { HIGH, PLM_NOFLAGS, 0, "/home/samba" }, + { HIGH, PLM_NOFLAGS, 0, "/usr/home/samba" }, + { LOW, PLM_NOFLAGS, 0, "/dev/printer" }, + { HIGH, PLM_CHILDOF, 0, "/var/log" }, + { LOW, PLM_NOFLAGS, 0, "/var/log/sendmail.st" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/var/run/utmp" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/var/log/lastlog" }, + { HIGH, PLM_NOFLAGS, LOWWRITE, "/var/log/wtmp" }, + { 0, 0, 0 } +}; + +#endif /* LOMAC_PLM_H */ diff --git a/sys/contrib/lomac/syscall_gate.c b/sys/contrib/lomac/syscall_gate.c new file mode 100644 index 0000000..d979263 --- /dev/null +++ b/sys/contrib/lomac/syscall_gate.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: syscall_gate.c,v 1.4 2001/11/05 21:23:12 bfeldman Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/syscall.h> +#include <sys/sysent.h> + +#include <machine/frame.h> + +#include "syscall_gate.h" +void syscall_gate_init(void); +int syscall_gate(struct thread *td, caddr_t params); + +static struct syscall_gate sg; + +void +syscall_gate_init(void) { + + sg.sg_table = curthread->td_proc->p_sysent->sv_table; + bzero(sg.sg_oldsyscalls, sizeof(sg.sg_oldsyscalls)); +} + +int +syscall_gate_register(int offset, sy_call_t *call, int narg, int mpsafe) { + struct sysent *se; + int error = 0; + + if (offset <= 0 || offset >= SYS_MAXSYSCALL) { + error = EINVAL; + goto out; + } + if (sg.sg_oldsyscalls[offset].sy_call != NULL) { + error = EEXIST; + goto out; + } + se = &sg.sg_table[offset]; + sg.sg_oldsyscalls[offset] = *se; + se->sy_call = (sy_call_t *)call; + se->sy_narg = narg | (mpsafe ? SYF_MPSAFE : 0); +out: + return (error); +} + +void +syscall_gate_deregister(int offset) { + KASSERT(offset > 0 && offset < SYS_MAXSYSCALL, ("syscall offset %d out of range", + offset)); + KASSERT(sg.sg_oldsyscalls[offset].sy_call != NULL, ("deregistering nonexistant syscall %d", + offset)); + sg.sg_table[offset] = sg.sg_oldsyscalls[offset]; + sg.sg_oldsyscalls[offset].sy_call = NULL; + sg.sg_oldsyscalls[offset].sy_narg = 0; +} + +static int +syscall_gate_modevent(module_t module, int event, void *unused) { + int i; + + switch ((enum modeventtype)event) { + case MOD_LOAD: + syscall_gate_init(); + break; + case MOD_UNLOAD: + for (i = 1; i < SYS_MAXSYSCALL; i++) { + struct sysent *se; + + se = &sg.sg_oldsyscalls[i]; + if (se->sy_call != NULL) + sg.sg_table[i] = *se; + } + break; + case MOD_SHUTDOWN: + break; + } + return (0); +} + +static moduledata_t syscall_gate_moduledata = { + "syscall_gate", + &syscall_gate_modevent, + NULL +}; +DECLARE_MODULE(syscall_gate, syscall_gate_moduledata, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(syscall_gate, 1); diff --git a/sys/contrib/lomac/syscall_gate.h b/sys/contrib/lomac/syscall_gate.h new file mode 100644 index 0000000..c6df862 --- /dev/null +++ b/sys/contrib/lomac/syscall_gate.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2001 Networks Associates Technology, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: syscall_gate.h,v 1.2 2001/09/20 17:47:46 bfeldman Exp $ + */ + +#ifndef SYSCALL_GATE_H +#define SYSCALL_GATE_H + +#include <sys/mutex.h> +#include <sys/sx.h> +#include <sys/sysent.h> + +#ifdef _KERNEL +struct syscall_gate { + struct sx sg_lock; /* syscalls entered into the kernel */ + struct sysent *sg_table; + struct sysent sg_oldsyscalls[SYS_MAXSYSCALL]; + struct sysent sg_newsyscalls[SYS_MAXSYSCALL]; +}; +#endif /* _KERNEL */ + +int syscall_gate_register(int offset, sy_call_t *func, int nargs, int mpsafe); +void syscall_gate_deregister(int offset); +#endif /* SYSCALL_GATE_H */ |