Changeset View
Changeset View
Standalone View
Standalone View
sys/security/mac_grantbylabel/mac_grantbylabel.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* $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 <sys/cdefs.h> | |||||
#include "opt_mac.h" | |||||
#include <sys/param.h> | |||||
#include <sys/capsicum.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/vnode.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/module.h> | |||||
#include <sys/mac.h> | |||||
#include <sys/namei.h> | |||||
#include <sys/priv.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/syslog.h> | |||||
#include <security/mac/mac_policy.h> | |||||
#include "mac_grantbylabel.h" | |||||
#include <security/mac_veriexec/mac_veriexec_internal.h> | |||||
#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); |