Index: etc/mtree/BSD.include.dist =================================================================== --- etc/mtree/BSD.include.dist +++ etc/mtree/BSD.include.dist @@ -290,6 +290,8 @@ .. mac_bsdextended .. + mac_grantbylabel + .. mac_lomac .. mac_mls Index: include/Makefile =================================================================== --- include/Makefile +++ include/Makefile @@ -59,7 +59,9 @@ netinet/netdump \ netinet/tcp_stacks \ security/audit \ - security/mac_biba security/mac_bsdextended security/mac_lomac \ + security/mac_biba security/mac_bsdextended \ + security/mac_grantbylabel \ + security/mac_lomac \ security/mac_mls security/mac_partition \ security/mac_veriexec \ sys/disk \ Index: lib/libveriexec/Makefile =================================================================== --- lib/libveriexec/Makefile +++ lib/libveriexec/Makefile @@ -1,6 +1,6 @@ # $FreeBSD$ -.include +.include LIB= veriexec MAN= veriexec.3 @@ -8,7 +8,10 @@ WARNS?= 2 -SRCS= veriexec_check.c +SRCS= \ + exec_script.c \ + gbl_check.c \ + veriexec_check.c .include Index: lib/libveriexec/exec_script.c =================================================================== --- /dev/null +++ lib/libveriexec/exec_script.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2019, Juniper Networks, 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "libveriexec.h" + +static char * +find_interpreter(const char *script) +{ + static const char ws[] = " \t\n\r"; + static char buf[MAXPATHLEN+4]; /* allow space for #! etc */ + char *cp; + int fd; + int n; + + cp = NULL; + if ((fd = open(script, O_RDONLY)) >= 0) { + if ((n = read(fd, buf, sizeof(buf))) > 0) { + if (strncmp(buf, "#!", 2) == 0) { + buf[sizeof(buf) - 1] = '\0'; + cp = &buf[2]; + if ((n = strspn(cp, ws)) > 0) + cp += n; + if ((n = strcspn(cp, ws)) > 0) { + cp[n] = '\0'; + } else { + cp = NULL; + } + } + } + close(fd); + } + return (cp); +} + +/** + * @brief exec a python or similar script + * + * Python and similar scripts must normally be signed and + * run directly rather than fed to the interpreter which + * is not normally allowed to be run directly. + * + * If direct execv of script fails due to EAUTH + * and process has GBL_VERIEXEC syslog event and run via + * interpreter. + * + * If interpreter is NULL look at first block of script + * to find ``#!`` magic. + * + * @prarm[in] interpreter + * if NULL, extract from script if necessary + * + * @prarm[in] argv + * argv for execv(2) + * argv[0] must be full path. + * Python at least requires argv[1] to also be the script path. + * + * @return + * error on failure usually EPERM or EAUTH + */ +int +execv_script(const char *interpreter, char * const *argv) +{ + const char *script; + int rc; + + script = argv[0]; + if (veriexec_check_path(script) == 0) { + rc = execv(script, argv); + } + /* still here? we might be allowed to run via interpreter */ + if (gbl_check_pid(0) & GBL_VERIEXEC) { + if (!interpreter) + interpreter = find_interpreter(script); + if (interpreter) { + syslog(LOG_NOTICE, "running %s via %s", + script, interpreter); + rc = execv(interpreter, argv); + } + } + return (rc); +} + +#if defined(MAIN) || defined(UNIT_TEST) +#include +#include + +int +main(int argc __unused, char *argv[]) +{ + const char *interp; + int c; + int s; + pid_t child; + + openlog("exec_script", LOG_PID|LOG_PERROR, LOG_DAEMON); + + interp = NULL; + while ((c = getopt(argc, argv, "i:")) != -1) { + switch (c) { + case 'i': + interp = optarg; + break; + default: + errx(1, "unknown option: -%c", c); + break; + } + } + argc -= optind; + argv += optind; + /* we need a child */ + child = fork(); + if (child < 0) + err(2, "fork"); + if (child == 0) { + c = execv_script(interp, argv); + err(2, "exec_script(%s,%s)", interp, argv[0]); + } + c = waitpid(child, &s, 0); + printf("%s: exit %d\n", argv[0], WEXITSTATUS(s)); + return (0); +} +#endif Index: lib/libveriexec/gbl_check.c =================================================================== --- /dev/null +++ lib/libveriexec/gbl_check.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, Juniper Networks, 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include + +#include +#include + +#include + +/** + * @brief does path have a gbl label + * + * @return + * @li 0 if no label or module not loaded + * @li value of label + */ +unsigned int +gbl_check_path(const char *path) +{ + struct mac_grantbylabel_fetch_gbl_args gbl; + int fd; + int rc; + + rc = 0; + if ((fd = open(path, O_RDONLY|O_VERIFY)) >= 0) { + gbl.u.fd = fd; + if (mac_syscall(MAC_GRANTBYLABEL_NAME, + MAC_GRANTBYLABEL_FETCH_GBL, + &gbl) == 0) { + rc = gbl.gbl; + } + close(fd); + } + return(rc); +} + +/** + * @brief does pid have a gbl label + * + * @return + * @li 0 if no label or module not loaded + * @li value of label + */ +unsigned int +gbl_check_pid(pid_t pid) +{ + struct mac_grantbylabel_fetch_gbl_args gbl; + int rc; + + rc = 0; + gbl.u.pid = pid; + if (mac_syscall(MAC_GRANTBYLABEL_NAME, + MAC_GRANTBYLABEL_FETCH_PID_GBL, &gbl) == 0) { + rc = gbl.gbl; + } + return(rc); +} Index: lib/libveriexec/libveriexec.h =================================================================== --- lib/libveriexec/libveriexec.h +++ lib/libveriexec/libveriexec.h @@ -29,9 +29,14 @@ #ifndef __LIBVERIEXEC_H__ #define __LIBVERIEXEC_H__ -int veriexec_check_fd_mode(int, unsigned int); -int veriexec_check_path_mode(const char *, unsigned int); -int veriexec_check_fd(int); -int veriexec_check_path(const char *); +int veriexec_check_fd_mode(int, unsigned int); +int veriexec_check_path_mode(const char *, unsigned int); +int veriexec_check_fd(int); +int veriexec_check_path(const char *); +unsigned int gbl_check_path(const char *); +unsigned int gbl_check_pid(pid_t); +int execv_script(const char *, char * const *); + +#define HAVE_GBL_CHECK_PID 1 #endif /* __LIBVERIEXEC_H__ */ Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3435,7 +3435,7 @@ dev/videomode/edid.c optional videomode dev/videomode/pickmode.c optional videomode dev/videomode/vesagtf.c optional videomode -dev/veriexec/verified_exec.c optional veriexec mac_veriexec +dev/veriexec/verified_exec.c optional mac_veriexec dev/vge/if_vge.c optional vge dev/viapm/viapm.c optional viapm pci dev/virtio/virtio.c optional virtio Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -826,6 +826,17 @@ oldtextvp = p->p_textvp; p->p_textvp = newtextvp; +#ifdef MAC + error = mac_proc_check_resource(p->p_ucred, p); + if (error) { + if (p->p_textvp) { + p->p_textvp = oldtextvp; + oldtextvp = NULL; + } + goto exec_fail_dealloc; + } +#endif + #ifdef KDTRACE_HOOKS /* * Tell the DTrace fasttrap provider about the exec if it Index: sys/modules/mac_grantbylabel/Makefile =================================================================== --- /dev/null +++ sys/modules/mac_grantbylabel/Makefile @@ -0,0 +1,36 @@ +# +# $Id: Makefile 472710 2011-10-13 03:43:06Z stevek $ +# +# Copyright (c) 2018, Juniper Networks, Inc. +# All rights reserved. +# + +.PATH: ${.PARSEDIR:H:H}/security/${.CURDIR:T} + +KMOD = ${.CURDIR:T} +SRCS = \ + vnode_if.h +SRCS += \ + opt_global.h \ + opt_mac.h \ + opt_veriexec.h +SRCS += \ + ${.CURDIR:T}.c \ + +.if defined(KERNBUILDDIR) +MKDEP= -include ${KERNBUILDDIR}/opt_global.h +.else +CFLAGS+= -include opt_global.h +MKDEP= -include opt_global.h +opt_mac.h: + echo "#define MAC_DEBUG 1" >> ${.TARGET} +opt_global.h: + echo "#define MAC 1" > ${.TARGET} +.endif + +.ifndef WITHOUT_MAC_GRANTBYLABEL_DEBUG +CFLAGS+= -DMAC_GRANTBYLABEL_DEBUG +.endif + +.include + Index: sys/modules/mac_grantbylabel/Makefile.depend =================================================================== --- /dev/null +++ sys/modules/mac_grantbylabel/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif Index: sys/security/mac/mac_framework.h =================================================================== --- sys/security/mac/mac_framework.h +++ sys/security/mac/mac_framework.h @@ -312,6 +312,7 @@ } int mac_proc_check_debug(struct ucred *cred, struct proc *p); +int mac_proc_check_resource(struct ucred *cred, struct proc *p); int mac_proc_check_sched(struct ucred *cred, struct proc *p); int mac_proc_check_signal(struct ucred *cred, struct proc *p, int signum); Index: sys/security/mac/mac_policy.h =================================================================== --- sys/security/mac/mac_policy.h +++ sys/security/mac/mac_policy.h @@ -389,6 +389,8 @@ typedef int (*mpo_proc_check_debug_t)(struct ucred *cred, struct proc *p); +typedef int (*mpo_proc_check_resource_t)(struct ucred *cred, + struct proc *proc); typedef int (*mpo_proc_check_sched_t)(struct ucred *cred, struct proc *p); typedef int (*mpo_proc_check_signal_t)(struct ucred *cred, @@ -833,6 +835,7 @@ mpo_priv_grant_t mpo_priv_grant; mpo_proc_check_debug_t mpo_proc_check_debug; + mpo_proc_check_resource_t mpo_proc_check_resource; mpo_proc_check_sched_t mpo_proc_check_sched; mpo_proc_check_signal_t mpo_proc_check_signal; mpo_proc_check_wait_t mpo_proc_check_wait; Index: sys/security/mac/mac_process.c =================================================================== --- sys/security/mac/mac_process.c +++ sys/security/mac/mac_process.c @@ -431,3 +431,18 @@ return (error); } + +MAC_CHECK_PROBE_DEFINE2(proc_check_resource, "struct ucred *", "struct proc *"); + +int +mac_proc_check_resource(struct ucred *cred, struct proc *p) +{ + int error; + + PROC_LOCK_ASSERT(p, MA_OWNED); + + MAC_POLICY_CHECK_NOSLEEP(proc_check_resource, cred, p); + MAC_CHECK_PROBE2(proc_check_resource, error, cred, p); + + return (error); +} Index: sys/security/mac_grantbylabel/mac_grantbylabel.h =================================================================== --- /dev/null +++ sys/security/mac_grantbylabel/mac_grantbylabel.h @@ -0,0 +1,66 @@ +/* + * $FreeBSD$ + * + * Copyright (c) 2018, Juniper Networks, 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef _SECURITY_MAC_GRANTBYLABEL_H +#define _SECURITY_MAC_GRANTBYLABEL_H + +#include + +#define MAC_GRANTBYLABEL_NAME "mac_grantbylabel" + +/* the bits we use to represent tokens */ +#define GBL_EMPTY (1<<0) +#define GBL_BIND (1<<1) +#define GBL_IPC (1<<2) +#define GBL_NET (1<<3) +#define GBL_PROC (1<<4) +#define GBL_RTSOCK (1<<5) +#define GBL_SYSCTL (1<<6) +#define GBL_VACCESS (1<<7) +#define GBL_VERIEXEC (1<<8) + +#define GBL_MAX 8 + +/* this should suffice for now */ +typedef uint32_t gbl_label_t; + +#define MAC_GRANTBYLABEL_FETCH_GBL 1 +#define MAC_GRANTBYLABEL_FETCH_PID_GBL 2 + +struct mac_grantbylabel_fetch_gbl_args { + union { + int fd; + pid_t pid; + } u; + gbl_label_t gbl; +}; + +#endif Index: sys/security/mac_grantbylabel/mac_grantbylabel.c =================================================================== --- /dev/null +++ sys/security/mac_grantbylabel/mac_grantbylabel.c @@ -0,0 +1,404 @@ +/*- + * $FreeBSD$ + * + * Copyright (c) 2018, Juniper Networks, 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#include + +#include "opt_mac.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mac_grantbylabel.h" +#include + +#define MAC_GRANTBYLABEL_FULLNAME "MAC/grantbylabel" + +#ifdef MAC_GRANTBYLABEL_DEBUG +# ifndef MAC_DEBUG +# define MAC_DEBUG +# endif +# define GRANTBYLABEL_DEBUG(n, x) if (mac_grantbylabel_debug >= (n)) printf x + +static int mac_grantbylabel_debug; + +SYSCTL_DECL(_security_mac); +SYSCTL_NODE(_security_mac, OID_AUTO, grantbylabel, CTLFLAG_RW, 0, + "MAC/grantbylabel policy controls"); +SYSCTL_INT(_security_mac_grantbylabel, OID_AUTO, debug, CTLFLAG_RW, + &mac_grantbylabel_debug, 0, "Debug mac_grantbylabel"); + +#else +# define GRANTBYLABEL_DEBUG(n, x) +#endif + +#ifdef MAC_DEBUG +#define MAC_GRANTBYLABEL_DBG(_lvl, _fmt, ...) \ + do { \ + GRANTBYLABEL_DEBUG((_lvl), (MAC_GRANTBYLABEL_FULLNAME ": " _fmt \ + "\n", ##__VA_ARGS__)); \ + } while(0) +#else +#define MAC_GRANTBYLABEL_DBG(_lvl, _fmt, ...) +#endif + + +/* label token prefix */ +#define GBL_PREFIX "gbl/" + +static int mac_grantbylabel_slot; + +#define SLOT(l) \ + mac_label_get((l), mac_grantbylabel_slot) +#define SLOT_SET(l, v) \ + mac_label_set((l), mac_grantbylabel_slot, (v)) + + +/** + * @brief parse label into bitmask + * + * We are only interested in tokens prefixed by GBL_PREFIX ("gbl/"). + * + * @return 32bit mask + */ +static gbl_label_t +gbl_parse_label(const char *label) +{ + gbl_label_t gbl; + char *cp; + + if (!(label && *label)) + return GBL_EMPTY; + gbl = 0; + for (cp = strstr(label, GBL_PREFIX); cp; cp = strstr(cp, GBL_PREFIX)) { + /* check we didn't find "fugbl/" */ + if (cp > label && cp[-1] != ',') { + cp += sizeof(GBL_PREFIX); + continue; + } + cp += sizeof(GBL_PREFIX) - 1; + switch (*cp) { + case 'b': + if (strncmp(cp, "bind", 4) == 0) + gbl |= GBL_BIND; + break; + case 'd': + if (strncmp(cp, "daemon", 6) == 0) + gbl |= (GBL_BIND|GBL_IPC|GBL_NET|GBL_PROC| + GBL_SYSCTL|GBL_VACCESS); + break; + case 'i': + if (strncmp(cp, "ipc", 3) == 0) + gbl |= GBL_IPC; + break; + case 'n': + if (strncmp(cp, "net", 3) == 0) + gbl |= GBL_NET; + break; + case 'p': + if (strncmp(cp, "proc", 4) == 0) + gbl |= GBL_PROC; + break; + case 'r': + if (strncmp(cp, "rtsock", 6) == 0) + gbl |= GBL_RTSOCK; + break; + case 's': + if (strncmp(cp, "sysctl", 6) == 0) + gbl |= GBL_SYSCTL; + break; + case 'v': + if (strncmp(cp, "vaccess", 7) == 0) + gbl |= GBL_VACCESS; + else if (strncmp(cp, "veriexec", 8) == 0) + gbl |= GBL_VERIEXEC; + break; + default: /* ignore unknown? */ + MAC_GRANTBYLABEL_DBG(1, + "ignoring unknown token at %s/%s", + GBL_PREFIX, cp); + break; + } + } + + return gbl; +} + + +/** + * @brief grant priv if warranted + * + * If the cred is root, we have nothing to do. + * Otherwise see if the current process has a label + * that grants it the requested priv. + */ +static int +mac_grantbylabel_priv_grant(struct ucred *cred, int priv) +{ + gbl_label_t label; + int rc; + + rc = EPERM; /* default response */ + + if ((curproc->p_flag & (P_KPROC|P_SYSTEM))) + return rc; /* not interested */ + + switch (priv) { + case PRIV_VERIEXEC_DIRECT: + case PRIV_VERIEXEC_NOVERIFY: + /* XXX might want to skip in FIPS mode */ + break; + default: + if (cred->cr_uid == 0) + return rc; /* not interested */ + break; + } + + label = (gbl_label_t)SLOT(curproc->p_textvp->v_label); + + /* + * We look at the extra privs granted + * via process label. + */ + switch (priv) { + case PRIV_IPC_READ: + case PRIV_IPC_WRITE: + if (label & GBL_IPC) + rc = 0; + break; + case PRIV_NETINET_BINDANY: + case PRIV_NETINET_RESERVEDPORT: /* socket bind low port */ + case PRIV_NETINET_REUSEPORT: + if (label & GBL_BIND) + rc = 0; + break; + case PRIV_NETINET_ADDRCTRL6: + case PRIV_NET_LAGG: + case PRIV_NET_SETIFFIB: + case PRIV_NET_SETIFVNET: + case PRIV_NETINET_SETHDROPTS: + case PRIV_NET_VXLAN: + case PRIV_NETINET_GETCRED: + case PRIV_NETINET_IPSEC: + case PRIV_NETINET_RAW: + if (label & GBL_NET) + rc = 0; + break; + case PRIV_NETINET_MROUTE: + case PRIV_NET_ROUTE: + if (label & GBL_RTSOCK) + rc = 0; + break; + case PRIV_PROC_LIMIT: + case PRIV_PROC_SETRLIMIT: + if (label & GBL_PROC) + rc = 0; + break; + case PRIV_SYSCTL_WRITE: + if (label & GBL_SYSCTL) + rc = 0; + break; + case PRIV_VFS_ADMIN: + case PRIV_VFS_BLOCKRESERVE: + case PRIV_VFS_CHOWN: + case PRIV_VFS_EXEC: /* vaccess file and accmode & VEXEC */ + case PRIV_VFS_GENERATION: + case PRIV_VFS_LOOKUP: /* vaccess DIR */ + case PRIV_VFS_READ: + case PRIV_VFS_WRITE: + if (label & GBL_VACCESS) + rc = 0; + break; + case PRIV_VERIEXEC_DIRECT: + /* + * We are here because we are attempting to direct exec + * something with the 'indirect' flag set. + * We need to check parent label for this one. + */ + PROC_LOCK(curproc); + label = (gbl_label_t)SLOT(curproc->p_pptr->p_textvp->v_label); + if (label & GBL_VERIEXEC) { + rc = 0; + /* + * Of course the only reason to be running an + * interpreter this way is to bypass O_VERIFY + * so we can run unsigned script. + * We set GBL_VERIEXEC on p_label for + * PRIV_VERIEXEC_NOVERIFY below + */ + SLOT_SET(curproc->p_label, GBL_VERIEXEC); + } + PROC_UNLOCK(curproc); + break; + case PRIV_VERIEXEC_NOVERIFY: + /* we look at p_label! see above */ + label = (gbl_label_t)SLOT(curproc->p_label); + if (label & GBL_VERIEXEC) + rc = 0; + break; + default: + break; + } + MAC_GRANTBYLABEL_DBG(rc ? 1 : 2, + "pid=%d priv=%d, label=%#o rc=%d", + curproc->p_pid, priv, label, rc); + + return rc; +} + + +/* + * If proc->p_textvp does not yet have a label, + * fetch file info from mac_veriexec + * and set label (if any) else set. + * If there is no label set it to GBL_EMPTY. + */ +static int +mac_grantbylabel_proc_check_resource(struct ucred *cred, + struct proc *proc) +{ + struct vattr va; + const char *label; + int error; + + if (!SLOT(proc->p_textvp->v_label)) { + error = VOP_GETATTR(proc->p_textvp, &va, cred); + if (!error) { + label = mac_veriexec_metadata_get_file_label(va.va_fsid, + va.va_fileid, va.va_gen, FALSE); + if (label) { + MAC_GRANTBYLABEL_DBG(1, + "label=%s dev=%ju, file %ju.%lu", + label, + (uintmax_t)va.va_fsid, + (uintmax_t)va.va_fileid, + va.va_gen); + SLOT_SET(proc->p_textvp->v_label, + (intptr_t)gbl_parse_label(label)); + return 0; + } + } + SLOT_SET(proc->p_textvp->v_label, + (intptr_t)GBL_EMPTY); + } + return 0; +} + +static int +mac_grantbylabel_syscall(struct thread *td, int call, void *arg) +{ + cap_rights_t rights; + struct mac_grantbylabel_fetch_gbl_args *gbl_args; + gbl_label_t gbl; + struct vattr va; + struct file *fp; + struct proc *proc; + const char *label; + int error; + + error = EOPNOTSUPP; + switch (call) { + case MAC_GRANTBYLABEL_FETCH_GBL: + gbl_args = arg; + gbl = 0; + error = getvnode(td, (uintptr_t) gbl_args->u.fd, + cap_rights_init(&rights), &fp); + if (error) + return (error); + + if (fp->f_type != DTYPE_VNODE) { + error = EINVAL; + goto cleanup_file; + } + + vn_lock(fp->f_vnode, LK_SHARED | LK_RETRY); + error = VOP_GETATTR(fp->f_vnode, &va, td->td_ucred); + if (!error) { + label = mac_veriexec_metadata_get_file_label(va.va_fsid, + va.va_fileid, va.va_gen, FALSE); + if (label) { + gbl = gbl_parse_label(label); + } else { + error = EOPNOTSUPP; + } + } + VOP_UNLOCK(fp->f_vnode); +cleanup_file: + fdrop(fp, td); + break; + case MAC_GRANTBYLABEL_FETCH_PID_GBL: + gbl_args = arg; + gbl = 0; + + /* right now we only support curproc */ + if (gbl_args->u.pid == 0 + || gbl_args->u.pid == curproc->p_pid) { + proc = curproc; + error = 0; + } else { + return (EINVAL); + } + gbl = SLOT(proc->p_textvp->v_label); + break; + } + if (error == 0) { + error = copyout(&gbl, &gbl_args->gbl, + sizeof(gbl_label_t)); + } + return error; +} + +static struct mac_policy_ops mac_grantbylabel_ops = +{ + .mpo_proc_check_resource = mac_grantbylabel_proc_check_resource, + .mpo_priv_grant = mac_grantbylabel_priv_grant, + .mpo_syscall = mac_grantbylabel_syscall, +}; + +#ifdef MAC_GRANTBYLABEL_DEBUG +# define MAC_GRANTBYLABEL_MPC_FLAGS MPC_LOADTIME_FLAG_UNLOADOK +#else +# define MAC_GRANTBYLABEL_MPC_FLAGS MPC_LOADTIME_FLAG_NOTLATE +#endif + +MAC_POLICY_SET(&mac_grantbylabel_ops, mac_grantbylabel, MAC_GRANTBYLABEL_FULLNAME, + MAC_GRANTBYLABEL_MPC_FLAGS, &mac_grantbylabel_slot); +MODULE_VERSION(mac_grantbylabel, 1); +MODULE_DEPEND(mac_grantbylabel, mac_veriexec, 1, 1, 1); Index: sys/security/mac_veriexec/mac_veriexec.h =================================================================== --- sys/security/mac_veriexec/mac_veriexec.h +++ sys/security/mac_veriexec/mac_veriexec.h @@ -157,6 +157,8 @@ int override); int mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen); +const char * mac_veriexec_metadata_get_file_label(dev_t fsid, long fileid, + unsigned long gen, int check_files); int mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p); #endif Index: sys/security/mac_veriexec/mac_veriexec.c =================================================================== --- sys/security/mac_veriexec/mac_veriexec.c +++ sys/security/mac_veriexec/mac_veriexec.c @@ -52,6 +52,7 @@ #include #include #include +#include #include "mac_veriexec.h" #include "mac_veriexec_internal.h" @@ -529,6 +530,10 @@ return (error); break; default: + if (mac_priv_grant(cred, PRIV_VERIEXEC_NOVERIFY) == 0) { + + return (0); + } /* * Caller wants open to fail unless there is a valid * fingerprint registered. Index: sys/security/mac_veriexec/veriexec_metadata.c =================================================================== --- sys/security/mac_veriexec/veriexec_metadata.c +++ sys/security/mac_veriexec/veriexec_metadata.c @@ -138,8 +138,6 @@ break; /* we need to garbage collect */ LIST_REMOVE(ip, entries); - if (ip->label) - free(ip->label, M_VERIEXEC); free(ip, M_VERIEXEC); } } @@ -584,6 +582,30 @@ } /** + * Return label if we have one + * + * @param fsid file system identifier to look for + * @param fileid file to look for + * @param gen generation of file + * @param check_files look at non-executable files? + * + * @return A pointer to the label or @c NULL + */ +const char * +mac_veriexec_metadata_get_file_label(dev_t fsid, long fileid, + unsigned long gen, int check_files) +{ + struct mac_veriexec_file_info *ip; + + ip = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, + NULL, check_files); + if (ip && (ip->flags & VERIEXEC_LABEL)) { + return ip->label; + } + return NULL; +} + +/** * Add a file and its fingerprint to the list of files attached * to the device @p fsid. * Index: sys/sys/priv.h =================================================================== --- sys/sys/priv.h +++ sys/sys/priv.h @@ -512,9 +512,16 @@ #define PRIV_KMEM_WRITE 681 /* Open mem/kmem for writing. */ /* + * veriexec override privileges - very rare! + */ +#define PRIV_VERIEXEC_DIRECT 700 /* Can override 'indirect' */ +#define PRIV_VERIEXEC_NOVERIFY 701 /* Can override O_VERIFY */ + + +/* * Track end of privilege list. */ -#define _PRIV_HIGHEST 682 +#define _PRIV_HIGHEST 702 /* * Validate that a named privilege is known by the privilege system. Invalid