# This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # mac_chkexec # mac_chkexec/mac_chkexec.c # mac_chkexec/mac_chkexec.h # echo c - mac_chkexec mkdir -p mac_chkexec > /dev/null 2>&1 echo x - mac_chkexec/mac_chkexec.c sed 's/^X//' >mac_chkexec/mac_chkexec.c << 'END-of-mac_chkexec/mac_chkexec.c' X/* X * Copyright (c) 2005 Christian S.J. Peron X * All rights reserved. X * X * Redistribution and use in source and binary forms, with or without X * modification, are permitted provided that the following conditions X * are met: X * 1. Redistributions of source code must retain the above copyright X * notice, this list of conditions and the following disclaimer. X * 2. Redistributions in binary form must reproduce the above copyright X * notice, this list of conditions and the following disclaimer in the X * documentation and/or other materials provided with the distribution. X * X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE X * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X * SUCH DAMAGE. X */ X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include X X#include X#include X#include X#include X X#include X X#include X#include X X#include "sha1.h" X X/* X * Prototypes X */ Xstatic int mac_chkexec_calc_vnode_md5(struct vnode *, X struct ucred *, u_char *); Xstatic int mac_chkexec_calc_vnode_sha1(struct vnode *, X struct ucred *, u_char *); Xstatic struct hash_algo X *mac_chkexec_get_algo(void); Xstatic int mac_chkexec_get_vcsum(struct vnode *, X struct mac_vcsum *); Xstatic int mac_chkexec_set_vcsum(struct vnode *, X struct mac_vcsum *); Xstatic int mac_chkexec_check(struct vnode *, struct ucred *); Xstatic int mac_chkexec_check_vnode_exec(struct ucred *, X struct vnode *, struct label *, X struct image_params *, struct label *); Xstatic int mac_chkexec_check_vnode_mmap(struct ucred *, X struct vnode *, struct label *, int, int); Xstatic int mac_chkexec_check_kld_load(struct ucred *, X struct vnode *, struct label *); Xstatic int mac_chkexec_vpcmp(struct vcache *, struct vcache *); Xstatic int sysctl_sethash(SYSCTL_HANDLER_ARGS); X Xstatic MALLOC_DEFINE(M_CHKEXEC, "mac_chkexec", "TrustedBSD trusted exec"); XSYSCTL_DECL(_security_mac); Xstatic SYSCTL_NODE(_security_mac, OID_AUTO, chkexec, CTLFLAG_RW, X 0, "mac_chkexec policy controls"); Xstatic int mac_chkexec_enable = 1; XSYSCTL_INT(_security_mac_chkexec, OID_AUTO, enable, X CTLFLAG_SECURE | CTLFLAG_RW, X &mac_chkexec_enable, 0, "enable trusted exec"); Xstatic int mac_chkexec_enforce; XSYSCTL_INT(_security_mac_chkexec, OID_AUTO, enforce, X CTLFLAG_SECURE | CTLFLAG_RW, X &mac_chkexec_enforce, 0, "enforce trusted exec policy"); Xstatic int mac_chkexec_ignore_untagged; XSYSCTL_INT(_security_mac_chkexec, OID_AUTO, ignore_untagged, X CTLFLAG_RW | CTLFLAG_SECURE, X &mac_chkexec_ignore_untagged, 0, ""); Xstatic int mac_csums_calculated; XSYSCTL_INT(_security_mac_chkexec, OID_AUTO, csums_calculated, X CTLFLAG_RD, &mac_csums_calculated, 0, ""); Xstatic SYSCTL_NODE(_security_mac_chkexec, OID_AUTO, cache, X CTLFLAG_RW, 0, "cache control OIDs for mac_chkexec"); Xstatic int mac_chkexec_cache = 1; XSYSCTL_INT(_security_mac_chkexec_cache, OID_AUTO, enable, X CTLFLAG_RW, &mac_chkexec_cache, 0, ""); Xstatic int mac_chkexec_cache_hits; XSYSCTL_INT(_security_mac_chkexec_cache, OID_AUTO, hits, X CTLFLAG_RD, &mac_chkexec_cache_hits, 0, ""); Xstatic int cache_vec_alloc = 512; XSYSCTL_INT(_security_mac_chkexec_cache, OID_AUTO, objmax, X CTLFLAG_RW, &cache_vec_alloc, 0, ""); Xstatic int cache_vec_used; XSYSCTL_INT(_security_mac_chkexec_cache, OID_AUTO, objused, X CTLFLAG_RD, &cache_vec_used, 0, ""); Xstatic int cache_invalidations; XSYSCTL_INT(_security_mac_chkexec_cache, OID_AUTO, invalidations, X CTLFLAG_RD, &cache_invalidations, 0, ""); Xstatic char hashalgo[32] = "sha1"; XSYSCTL_STRING(_security_mac_chkexec, OID_AUTO, algo, X CTLFLAG_SECURE | CTLFLAG_RW, X hashalgo, sizeof(hashalgo), "Current trusted exec algorithm"); XSYSCTL_NODE(_security_mac_chkexec, OID_AUTO, sethash, X CTLFLAG_RW, sysctl_sethash, "TrustedBSD chkexec sethash"); X Xstatic struct hash_algo ha_table[] = { X { mac_chkexec_calc_vnode_sha1, 20, MAC_VCSUM_SHA1, "sha1" }, X { mac_chkexec_calc_vnode_md5, 16, MAC_VCSUM_MD5, "md5" }, X { NULL, 0, 0, NULL }, X}; X XRB_PROTOTYPE(btree, vcache, glue, mac_chkexec_vpcmp); XRB_GENERATE(btree, vcache, glue, mac_chkexec_vpcmp); XTAILQ_HEAD(tailhead, vcache_fs) cache_head = X TAILQ_HEAD_INITIALIZER(cache_head); Xstatic struct mtx cache_mtx; Xstatic uma_zone_t cache_zone; X Xstatic int Xsysctl_sethash(SYSCTL_HANDLER_ARGS) X{ X struct nameidata nd; X char *pathname; X int error; X struct hash_algo *ha; X struct mac_vcsum vcsum; X u_char digest[MAXCSUMSIZE]; X X if (!mac_chkexec_enable) X return (0); X error = suser(curthread); X if (error || mac_chkexec_enforce) X return (error); X if (req->newptr) { X ha = mac_chkexec_get_algo(); X if (ha == NULL) { X#ifdef DEBUG X printf("mac_chkexec_check: invalid checksum algorithm\n"); X#endif X return (EPERM); X } X error = SYSCTL_IN(req, &pathname, sizeof(pathname)); X if (error) X return (error); X NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, X UIO_USERSPACE, pathname, curthread); X if ((error = namei(&nd)) != 0) X return (error); X error = ha->crypto_hash(nd.ni_vp, X curthread->td_ucred, digest); X if (error) { X NDFREE(&nd, NDF_ONLY_PNBUF); X vput(nd.ni_vp); X return (error); X } X memcpy(vcsum.vs_sum, digest, ha->hashsize); X vcsum.vs_flags = ha->hashmask; X error = mac_chkexec_set_vcsum(nd.ni_vp, &vcsum); X NDFREE(&nd, NDF_ONLY_PNBUF); X vput(nd.ni_vp); X return (error); X } X return (0); X} X X/* X * File ID comparison function. This function will be used X * by the red/black binary search tree operations for caching. X */ Xstatic int Xmac_chkexec_vpcmp(struct vcache *vc1, struct vcache *vc2) X{ X X if (vc1->fileid > vc2->fileid) X return (1); X if (vc1->fileid < vc2->fileid) X return (-1); X return (0); X} X Xstatic void Xmac_chkexec_init(struct mac_policy_conf *conf) X{ X X mtx_init(&cache_mtx, "lock for per device binary search trees", X NULL, MTX_DEF); X TAILQ_INIT(&cache_head); X cache_zone = uma_zcreate("MAC trusted exec cache zone", X sizeof(struct vcache), NULL, NULL, NULL, X NULL, UMA_ALIGN_PTR, 0); X KASSERT(cache_zone != NULL, ("uma_zcreate returned NULL")); X} X Xstatic void Xmac_chkexec_destroy(struct mac_policy_conf *conf) X{ X struct vcache *vcp, *next_vcp; X struct vcache_fs *vfc, *vfc2; X X mtx_lock(&cache_mtx); X TAILQ_FOREACH_SAFE(vfc, &cache_head, glue, vfc2) { X mtx_lock(&vfc->btree_mtx); X for (vcp = RB_ROOT(&vfc->btree); vcp; X vcp = next_vcp) { X next_vcp = RB_NEXT(btree, &vfc->btree, vcp); X RB_REMOVE(btree, &vfc->btree, vcp); X uma_zfree(cache_zone, vcp); X cache_vec_used--; X } X mtx_unlock(&vfc->btree_mtx); X mtx_destroy(&vfc->btree_mtx); X TAILQ_REMOVE(&cache_head, vfc, glue); X free(vfc, M_CHKEXEC); X } X mtx_unlock(&cache_mtx); X mtx_destroy(&cache_mtx); X KASSERT(cache_zone != NULL, ("destroying null cache zone")); X uma_zdestroy(cache_zone); X} X X/* XXX reference counting should be used here */ X/* Retrieve the cache associated with the filesystem ID stored in X * the vnode. If a cache is not present, create one and return it. X */ Xstatic struct vcache_fs * Xmac_chkexec_get_fs_cache(struct vnode *vp) X{ X struct vcache_fs *vfc; X struct vattr va, *vap; X int error; X X ASSERT_VOP_LOCKED(vp, "mac_chkexec_get_fs_cache: no vlock held"); X vap = &va; X error = VOP_GETATTR(vp, vap, NOCRED, curthread); X if (error) X return (NULL); X mtx_lock(&cache_mtx); X TAILQ_FOREACH(vfc, &cache_head, glue) X if (vfc->fsid == vap->va_fsid) { X mtx_unlock(&cache_mtx); X return (vfc); X } X mtx_unlock(&cache_mtx); X vfc = malloc(sizeof(*vfc), M_CHKEXEC, M_WAITOK | M_ZERO); X vfc->fsid = vap->va_fsid; X mtx_init(&vfc->btree_mtx, "binary search tree lock", X NULL, MTX_DEF); X RB_INIT(&vfc->btree); X mtx_lock(&cache_mtx); X TAILQ_INSERT_HEAD(&cache_head, vfc, glue); X mtx_unlock(&cache_mtx); X return (vfc); X} X X/* Given a vnode and a cryptographic checksum, store it in the X * per filesystem cache. Allow at most security.mac.chkexec.cache.objmax X * elements to be cached. X */ Xstatic void Xmac_chkexec_cache_vcsum(struct vnode *vp, u_char *digest) X{ X struct vcache_fs *vfc; X struct vcache *vcp; X int error; X struct vattr *vap, va; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X if (!mac_chkexec_cache) X return; X if ((cache_vec_used + 1) > cache_vec_alloc) X return; X vfc = mac_chkexec_get_fs_cache(vp); X if (vfc == NULL) X return; X vap = &va; X error = VOP_GETATTR(vp, vap, NOCRED, curthread); X if (error) X return; X vcp = uma_zalloc(cache_zone, M_WAITOK); X vcp->fileid = vap->va_fileid; X memcpy(vcp->digest, digest, MAXCSUMSIZE); X mtx_lock(&cache_mtx); X mtx_lock(&vfc->btree_mtx); X if (RB_INSERT(btree, &vfc->btree, vcp) != NULL) { X mtx_unlock(&vfc->btree_mtx); X mtx_unlock(&cache_mtx); X uma_zfree(cache_zone, vcp); X#ifdef DEBUG X printf("mac_chkexec_cache_vcsum: element collision\n"); X#endif X return; X } X cache_vec_used++; X mtx_unlock(&vfc->btree_mtx); X mtx_unlock(&cache_mtx); X} X X/* If an inode changes, we will want to invalidate the cache item X * associated with it. Otherwise this could result in the execution X * of a "used to be trusted, but not anymore" binary. We must be sure X * that we hook any system calls which can modify the contents of the X * file in anyway. X */ Xstatic void Xmac_chkexec_cache_invalidate(struct vnode *vp) X{ X struct vcache vc, *vcp; X struct vcache_fs *vfc; X struct vattr *vap, va; X int error; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X vfc = mac_chkexec_get_fs_cache(vp); X if (vfc == NULL) X return; X vap = &va; X error = VOP_GETATTR(vp, vap, NOCRED, curthread); X if (error) X return; X vc.fileid = vap->va_fileid; X mtx_lock(&vfc->btree_mtx); X vcp = RB_FIND(btree, &vfc->btree, &vc); X if (vcp == NULL) { X mtx_unlock(&vfc->btree_mtx); X return; X } X RB_REMOVE(btree, &vfc->btree, vcp); X cache_vec_used--; X cache_invalidations++; X mtx_unlock(&vfc->btree_mtx); X uma_zfree(cache_zone, vcp); X} X X/* Given a vnode, retrieve the per filesystem cache and do a search X * for the inode. If the item is not found, return NULL and let X * the caller decide how to handle it, otherwise return a pointer X * to the vcache item. X */ Xstatic struct vcache * Xmac_chkexec_cache_find(struct vnode *vp) X{ X struct vcache *vcp, vc; X int error; X struct vcache_fs *vfc; X struct vattr va, *vap; X X if (!mac_chkexec_cache) X return (NULL); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X vap = &va; X error = VOP_GETATTR(vp, &va, NOCRED, curthread); X if (error) X return (NULL); X vfc = mac_chkexec_get_fs_cache(vp); X if (vfc == NULL) X return (NULL); X vc.fileid = vap->va_fileid; X mtx_lock(&vfc->btree_mtx); X vcp = RB_FIND(btree, &vfc->btree, &vc); X mtx_unlock(&vfc->btree_mtx); X if (vcp) X mac_chkexec_cache_hits++; X return (vcp); X} X Xstatic struct hash_algo * Xmac_chkexec_get_algo(void) X{ X struct hash_algo *ha; X X for (ha = &ha_table[0]; ha->hashname != NULL; ha++) { X KASSERT(ha->hashsize <= MAXCSUMSIZE, X ("hashsize too big for buffer")); X if (strcmp(hashalgo, ha->hashname) == 0) X return (ha); X } X return (NULL); X} X Xstatic int Xmac_chkexec_validate(struct mac_vcsum *vsum) X{ X X switch (vsum->vs_flags) { X case MAC_VCSUM_SHA1: X case MAC_VCSUM_MD5: X return (0); X } X return (EINVAL); X} X Xstatic int Xmac_chkexec_get_vcsum(struct vnode *vp, struct mac_vcsum *vsum) X{ X struct thread *td; X int error, attrlen; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X td = curthread; X attrlen = sizeof(*vsum); X error = vn_extattr_get(vp, IO_NODELOCKED, MAC_CHKEXEC_ATTRN, X MAC_CHKEXEC, &attrlen, (caddr_t)vsum, td); X if (error) X return (error); X error = mac_chkexec_validate(vsum); X if (error) X return (error); X if (attrlen != sizeof(*vsum)) { X#ifdef DEBUG X printf("mac_chkexec_get_vcsum: invalid attribute size %d\n", X attrlen); X#endif X return (EPERM); X } X return (error); X} X Xstatic int Xmac_chkexec_set_vcsum(struct vnode *vp, struct mac_vcsum *vsum) X{ X struct thread *td; X int error; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X td = curthread; X error = vn_extattr_set(vp, IO_NODELOCKED, MAC_CHKEXEC_ATTRN, X MAC_CHKEXEC, sizeof(*vsum), (caddr_t)vsum, td); X return (error); X} X X/* The checksum calculation code is reminiscent of the code found X * in NetBSD "verified exec" with some additional error checking. X */ Xstatic int Xmac_chkexec_calc_vnode_md5(struct vnode *vp, struct ucred *cred, X u_char *digest) X{ X struct thread *td; X MD5_CTX md5ctx; X u_quad_t b; X int error, count; X size_t resid; X struct vattr va; X caddr_t bufobj; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X KASSERT(vp != NULL, ("mac_calc_vnode_md5: NULL vnode pointer")); X td = curthread; X error = VOP_GETATTR(vp, &va, cred, td); X if (error) X return (error); X bufobj = malloc(PAGE_SIZE, M_CHKEXEC, M_WAITOK); X MD5Init(&md5ctx); X for (b = 0; b < va.va_size; b += PAGE_SIZE) { X if ((PAGE_SIZE + b) > va.va_size) X count = va.va_size - b; X else X count = PAGE_SIZE; X error = vn_rdwr(UIO_READ, vp, bufobj, count, b, X UIO_SYSSPACE, IO_NODELOCKED, cred, NOCRED, X &resid, td); X if (error) { X free(bufobj, M_CHKEXEC); X return (error); X } X if (resid != 0) { X free(bufobj, M_CHKEXEC); X return (EIO); X } X MD5Update(&md5ctx, bufobj, (u_int)count); X } X free(bufobj, M_CHKEXEC); X MD5Final(digest, &md5ctx); X mac_csums_calculated++; X return (0); X} X Xstatic int Xmac_chkexec_calc_vnode_sha1(struct vnode *vp, struct ucred *cred, X u_char *digest) X{ X struct thread *td; X SHA1_CTX sha1ctx; X u_quad_t b; X int error, count; X size_t resid; X struct vattr va; X caddr_t bufobj; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X KASSERT(vp != NULL, ("mac_calc_vnode_sha1: NULL vnode pointer")); X td = curthread; X error = VOP_GETATTR(vp, &va, cred, td); X if (error) X return (error); X bufobj = malloc(PAGE_SIZE, M_CHKEXEC, M_WAITOK); X SHA1Init(&sha1ctx); X for (b = 0; b < va.va_size; b += PAGE_SIZE) { X if ((PAGE_SIZE + b) > va.va_size) X count = va.va_size - b; X else X count = PAGE_SIZE; X error = vn_rdwr(UIO_READ, vp, bufobj, count, b, X UIO_SYSSPACE, IO_NODELOCKED, cred, NOCRED, X &resid, td); X if (error) { X free(bufobj, M_CHKEXEC); X return (error); X } X if (resid != 0) { X free(bufobj, M_CHKEXEC); X return (EIO); X } X SHA1Update(&sha1ctx, bufobj, (u_int)count); X } X free(bufobj, M_CHKEXEC); X SHA1Final(digest, &sha1ctx); X mac_csums_calculated++; X return (0); X} X Xstatic int Xmac_chkexec_check_depends(struct vnode *vp, struct ucred *cred) X{ X char *depends, **ap, *paths[10]; X int error, i, npaths; X struct nameidata nd; X size_t alen; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = VOP_GETEXTATTR(vp, MAC_CHKEXEC_ATTRN, "chkexec_depend", X NULL, &alen, NOCRED, curthread); X if (error && error == ENOATTR) X return (0); X else if (error) X return (error); X depends = malloc(alen + 1, M_CHKEXEC, M_WAITOK | M_ZERO); X error = vn_extattr_get(vp, IO_NODELOCKED, MAC_CHKEXEC_ATTRN, X "chkexec_depend", &alen, depends, curthread); X for (npaths = 0, ap = paths; X (*ap = strsep(&depends, ":")) != NULL; npaths++) X if (**ap != '\0') X if (++ap >= &paths[10]) X break; X for (i = 0; i < npaths; i++) { X NDINIT(&nd, LOOKUP, LOCKLEAF | FOLLOW, X UIO_SYSSPACE, paths[i], curthread); X if ((error = namei(&nd)) != 0) { X free(depends, M_CHKEXEC); X return (error); X } X error = mac_chkexec_check(nd.ni_vp, cred); X NDFREE(&nd, NDF_ONLY_PNBUF); X vput(nd.ni_vp); X if (error) { X free(depends, M_CHKEXEC); X return (error); X } X } X free(depends, M_CHKEXEC); X return (0); X} X Xstatic int Xmac_chkexec_check(struct vnode *vp, struct ucred *cred) X{ X struct mac_vcsum vcsum; X int match, error; X struct hash_algo *ha; X u_char digest[MAXCSUMSIZE]; X struct vcache *vcp; X struct mount *mp; X X ASSERT_VOP_LOCKED(vp, "no vlock held"); X /* XXXCHKEXEC Check to see if the filesystem is read only, if it is X * and we are not enforcing the policy, the policy will attempt to X * update the filesystem which will be futile. Instead print a message X * to the console and grant access. X */ X mp = vp->v_mount; X KASSERT(mp != NULL, X ("mac_chkexec NULL mount point for vnode")); X if ((mp->mnt_flag & MNT_RDONLY) == 1 && !mac_chkexec_enforce) X return (0); X /* We are only interested in the execution of regular files */ X if (vp->v_type != VREG) X return (0); X /* X * Retrieve the algorithm specified in the sysctl OID. By default X * we leave this as SHA1. If the algorithm is invalid, deny access X * since we have no way to verify the file's integrity. X */ X ha = mac_chkexec_get_algo(); X if (ha == NULL) { X#ifdef DEBUG X printf("mac_chkexec_check: invalid checksum algorithm\n"); X#endif X return (EPERM); X } X /* X * If retrieving of the checksum stored in the file's extended X * attribute fails, we have no way of verifying this files integrity. X * Thus we will deny access to this file, erroring on the side of security. X */ X error = mac_chkexec_get_vcsum(vp, &vcsum); X if (error && error != ENOATTR) X return (error); X error = mac_chkexec_check_depends(vp, cred); X if (error && mac_chkexec_enforce) X return (error); X /* X * If no checksum is present in the file, and we are ignoring X * un-tagged vnodes, grant execution access. Otherwise if the policy X * is being enforced, deny access. X * X * If the system is in "learning" mode, that is, if we are not X * enforcing the policy but it's enabled, then set the current X * file's checksum. X */ X if (error == ENOATTR) { X if (mac_chkexec_ignore_untagged) X return (0); X if (mac_chkexec_enforce) { X#ifdef DEBUG X printf("mac_chkexec: un-registered vnode while policy enforced\n"); X#endif X return (EPERM); X } X error = ha->crypto_hash(vp, cred, digest); X if (error) X return (error); X memcpy(vcsum.vs_sum, digest, ha->hashsize); X vcsum.vs_flags = ha->hashmask; X error = mac_chkexec_set_vcsum(vp, &vcsum); X return (error); X } X /* X * To improve performance, see if we have already cached the X * checksum for this inode. If not, then compute the checksum X * and create the cache entry. X */ X vcp = mac_chkexec_cache_find(vp); X if (vcp == NULL) { X error = ha->crypto_hash(vp, cred, digest); X if (error) X return (error); X mac_chkexec_cache_vcsum(vp, digest); X } else X memcpy(digest, vcp->digest, MAXCSUMSIZE); X match = (memcmp(digest, vcsum.vs_sum, X ha->hashsize) == 0); X if (!match && !mac_chkexec_enforce) { X memcpy(vcsum.vs_sum, digest, ha->hashsize); X vcsum.vs_flags = ha->hashmask; X error = mac_chkexec_set_vcsum(vp, &vcsum); X return (error); X } X#ifdef DEBUG X if (!match) X printf("mac_chkexec: checksum mismatch, denying\n"); X#endif X return (!match ? EPERM : 0); X} X Xstatic int Xmac_chkexec_check_vnode_exec(struct ucred *cred, struct vnode *vp, X struct label *label, struct image_params *imgp, X struct label *execlabel) X{ X int error; X X if (!mac_chkexec_enable) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = mac_chkexec_check(vp, cred); X return (error); X} X Xstatic int Xmac_chkexec_check_vnode_mmap(struct ucred *cred, struct vnode *vp, X struct label *label, int prot, int flags) X{ X int error; X X if (!mac_chkexec_enable) X return (0); X /* XXX we make the assumption that the run-time linker in userspace X * will be setting the appropriate permissions of this mapping. Just X */ X if ((prot & PROT_EXEC) == 0) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = mac_chkexec_check(vp, cred); X return (error); X} X Xstatic int Xmac_chkexec_check_kld_load(struct ucred *cred, struct vnode *vp, X struct label *vlabel) X{ X int error; X X if (!mac_chkexec_enable) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = mac_chkexec_check(vp, cred); X return (error); X} X X/* X * Hook certain operations from userspace which can result in in objects X * being modified. Although we allow the modifications to these objects X * we need to ensure that we have invalidated any cache entries associated X * with this inode. X */ Xstatic int Xmac_chkexec_check_vnode_open(struct ucred *cred, struct vnode *vp, X struct label *vnodelabel, int acc_mode) X{ X X if (!mac_chkexec_enable) X return (0); X if ((acc_mode & (VWRITE | VAPPEND | VADMIN)) == 0) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X mac_chkexec_cache_invalidate(vp); X return (0); X} X Xstatic int Xmac_chkexec_check_vnode_delete(struct ucred *cred, struct vnode *dvp, X struct label *dlabel, struct vnode *vp, struct label *label, X struct componentname *cnp) X{ X struct mac_vcsum vsum; X int error; X X if (!mac_chkexec_enable) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = mac_chkexec_get_vcsum(vp, &vsum); X if (error) X return (0); X mac_chkexec_cache_invalidate(vp); X return (0); X} X X/* X * If the subject is asking if they have execute permissions on a certain X * object, compute the checksum of the object and check the integrity. X * If the checksums do not match, deny access. X */ Xstatic int Xmac_chkexec_check_vnode_access(struct ucred *cred, struct vnode *vp, X struct label *label, int acc_mode) X{ X int error; X X if (!mac_chkexec_enable) X return (0); X if ((acc_mode & VEXEC) == 0) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X error = mac_chkexec_check(vp, cred); X return (error); X} X X/* X * Hook calls to setextattr and deleteextattr from userspace. If the policy X * is loaded and the subject is attempting to change the namespace associated X * with storing the checksums, deny access. X */ Xstatic int Xmac_chkexec_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, X struct label *vnodelabel, int attrnamespace, const char *name, X struct uio *uio) X{ X X if (!mac_chkexec_enable) X return (0); X if (attrnamespace != MAC_CHKEXEC_ATTRN) X return (0); X if (mac_chkexec_enforce) { X#ifdef DEBUG X printf("mac_chkexec: can not setextattr on namespace while policy is loaded\n"); X#endif X return (EPERM); X } X return (0); X} X Xstatic int Xmac_chkexec_check_vnode_deleteextattr(struct ucred *cred, struct vnode *vp, X struct label *label, int attrnamespace, const char *name) X{ X X if (!mac_chkexec_enable) X return (0); X if (attrnamespace != MAC_CHKEXEC_ATTRN) X return (0); X if (mac_chkexec_enforce) { X#ifdef DEBUG X printf("mac_chkexec: can not delextattr on namespace while policy is loaded\n"); X#endif X return (EPERM); X } X return (0); X} X Xstatic int Xmac_chkexec_check_vnode_write(struct ucred *cred, struct ucred *fcred, X struct vnode *vp, struct label *label) X{ X X if (!mac_chkexec_enable) X return (0); X ASSERT_VOP_LOCKED(vp, "no vlock held"); X mac_chkexec_cache_invalidate(vp); X return (0); X} X Xstatic struct mac_policy_ops mac_chkexec_ops = X{ X .mpo_init = mac_chkexec_init, X .mpo_destroy = mac_chkexec_destroy, X .mpo_check_vnode_exec = mac_chkexec_check_vnode_exec, X .mpo_check_vnode_mmap = mac_chkexec_check_vnode_mmap, X .mpo_check_kld_load = mac_chkexec_check_kld_load, X .mpo_check_vnode_open = mac_chkexec_check_vnode_open, X .mpo_check_vnode_delete = mac_chkexec_check_vnode_delete, X .mpo_check_vnode_access = mac_chkexec_check_vnode_access, X .mpo_check_vnode_deleteextattr = mac_chkexec_check_vnode_deleteextattr, X .mpo_check_vnode_setextattr = mac_chkexec_check_vnode_setextattr, X .mpo_check_vnode_write = mac_chkexec_check_vnode_write, X}; X XMAC_POLICY_SET(&mac_chkexec_ops, mac_chkexec, "TrustedBSD MAC/trusted exec", X MPC_LOADTIME_FLAG_UNLOADOK, NULL); END-of-mac_chkexec/mac_chkexec.c echo x - mac_chkexec/mac_chkexec.h sed 's/^X//' >mac_chkexec/mac_chkexec.h << 'END-of-mac_chkexec/mac_chkexec.h' X/*- X * Copyright (c) 2005 Christian S.J. Peron X * All rights reserved. X * X * Redistribution and use in source and binary forms, with or without X * modification, are permitted provided that the following conditions X * are met: X * 1. Redistributions of source code must retain the above copyright X * notice, this list of conditions and the following disclaimer. X * 2. Redistributions in binary form must reproduce the above copyright X * notice, this list of conditions and the following disclaimer in the X * documentation and/or other materials provided with the distribution. X * X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE X * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X * SUCH DAMAGE. X */ X#ifndef MAC_CHKEXEC_H_ X#define MAC_CHKEXEC_H_ X X#define MAC_VCSUM_MD5 0x00000001 X#define MAC_VCSUM_SHA1 0x00000002 X#define MAC_CHKEXEC_ATTRN EXTATTR_NAMESPACE_SYSTEM X#define MAC_CHKEXEC "chkexec" X#define MAXCSUMSIZE 32 X X#ifdef _KERNEL Xstruct vcache { X RB_ENTRY(vcache) glue; X u_long fileid; X u_char digest[MAXCSUMSIZE]; X}; X Xstruct vcache_fs { X RB_HEAD(btree, vcache) btree; X struct mtx btree_mtx; X dev_t fsid; X TAILQ_ENTRY(vcache_fs) glue; X}; X#endif /* _KERNEL */ X Xstruct mac_vcsum { X u_char vs_sum[32]; /* vnode checksum */ X int vs_flags; X}; X Xstruct ucred; Xstruct vnode; Xstruct hash_algo { X int (*crypto_hash)(struct vnode *, struct ucred *, u_char *); X size_t hashsize; /* size of message digest */ X int hashmask; /* hash algorithm mask */ X char *hashname; /* name of hashing algorithm */ X}; X#endif /* MAC_CHCKEXEC_H_ */ END-of-mac_chkexec/mac_chkexec.h exit