2018-06-20 00:48:46 +00:00
|
|
|
/*
|
|
|
|
*
|
2023-04-02 21:58:27 +00:00
|
|
|
* Copyright (c) 2011-2023, Juniper Networks, Inc.
|
2018-06-20 00:48:46 +00:00
|
|
|
* 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 ``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/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/ioccom.h>
|
|
|
|
#include <sys/jail.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/mdioctl.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/namei.h>
|
2019-05-17 18:02:26 +00:00
|
|
|
#include <sys/priv.h>
|
2018-06-20 00:48:46 +00:00
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
|
|
|
|
#include <security/mac_veriexec/mac_veriexec.h>
|
|
|
|
#include <security/mac_veriexec/mac_veriexec_internal.h>
|
|
|
|
|
|
|
|
#include "veriexec_ioctl.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need a mutex while updating lists etc.
|
|
|
|
*/
|
|
|
|
extern struct mtx ve_mutex;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the ioctl for the device
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
verifiedexecioctl(struct cdev *dev __unused, u_long cmd, caddr_t data,
|
|
|
|
int flags, struct thread *td)
|
|
|
|
{
|
|
|
|
struct nameidata nid;
|
|
|
|
struct vattr vattr;
|
2019-05-17 19:27:07 +00:00
|
|
|
struct verified_exec_label_params *lparams;
|
2023-04-02 21:58:27 +00:00
|
|
|
struct verified_exec_params *params, params_;
|
2018-06-20 00:48:46 +00:00
|
|
|
int error = 0;
|
|
|
|
|
2019-05-17 18:02:26 +00:00
|
|
|
/*
|
|
|
|
* These commands are considered safe requests for anyone who has
|
|
|
|
* permission to access to device node.
|
|
|
|
*/
|
|
|
|
switch (cmd) {
|
|
|
|
case VERIEXEC_GETSTATE:
|
|
|
|
{
|
|
|
|
int *ip = (int *)data;
|
|
|
|
|
|
|
|
if (ip)
|
|
|
|
*ip = mac_veriexec_get_state();
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Anything beyond this point is considered dangerous, so we need to
|
|
|
|
* only allow processes that have kmem write privs to do them.
|
|
|
|
*
|
|
|
|
* MAC/veriexec will grant kmem write privs to "trusted" processes.
|
|
|
|
*/
|
veriexec: Additional functionality for MAC/veriexec
Ensure veriexec opens the file before doing any read operations.
When the MAC_VERIEXEC_CHECK_PATH_SYSCALL syscall is requested, veriexec
needs to open the file before calling mac_veriexec_check_vp. This is to
ensure any set up is done by the file system. Most file systems do not
explicitly need an open, but some (e.g. virtfs) require initialization
of access tokens (file identifiers, etc.) before doing any read or write
operations.
The evaluate_fingerprint() function needs to ensure it has an open file
for reading in order to evaluate the fingerprint. The ideal solution is
to have a hook after the VOP_OPEN call in vn_open. For now, we open the
file for reading, envaluate the fingerprint, and close the file. While
this leaves a potential hole that could possibly be taken advantage of
by a dedicated aversary, this code path is not typically visited often
in our use cases, as we primarily encounter verified mounts and not
individual files. This should be considered a temporary workaround until
discussions about the post-open hook have concluded and the hook becomes
available.
Add MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL and
MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL to mac_veriexec_syscall so we can
fetch and check label contents in an unconstrained manner.
Add a check for PRIV_VERIEXEC_CONTROL to do ioctl on /dev/veriexec
Make it clear that trusted process cannot be debugged. Attempts to debug
a trusted process already fail, but the failure path is very obscure.
Add an explicit check for VERIEXEC_TRUSTED in
mac_veriexec_proc_check_debug.
We need mac_veriexec_priv_check to not block PRIV_KMEM_WRITE if
mac_priv_gant() says it is ok.
Reviewed by: sjg
Obtained from: Juniper Networks, Inc.
2023-04-02 19:33:10 +00:00
|
|
|
error = priv_check(td, PRIV_VERIEXEC_CONTROL);
|
2019-05-17 18:02:26 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
|
2019-05-17 19:27:07 +00:00
|
|
|
lparams = (struct verified_exec_label_params *)data;
|
2023-04-02 21:58:27 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case VERIEXEC_LABEL_LOAD:
|
2019-05-17 19:27:07 +00:00
|
|
|
params = &lparams->params;
|
2023-04-02 21:58:27 +00:00
|
|
|
break;
|
|
|
|
case VERIEXEC_SIGNED_LOAD32:
|
|
|
|
params = ¶ms_;
|
|
|
|
memcpy(params, data, sizeof(struct verified_exec_params32));
|
|
|
|
break;
|
|
|
|
default:
|
2019-05-17 19:27:07 +00:00
|
|
|
params = (struct verified_exec_params *)data;
|
2023-04-02 21:58:27 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-05-17 19:27:07 +00:00
|
|
|
|
2018-06-20 00:48:46 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case VERIEXEC_ACTIVE:
|
|
|
|
mtx_lock(&ve_mutex);
|
|
|
|
if (mac_veriexec_in_state(VERIEXEC_STATE_LOADED))
|
|
|
|
mac_veriexec_set_state(VERIEXEC_STATE_ACTIVE);
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
mtx_unlock(&ve_mutex);
|
|
|
|
break;
|
|
|
|
case VERIEXEC_DEBUG_ON:
|
|
|
|
mtx_lock(&ve_mutex);
|
|
|
|
{
|
|
|
|
int *ip = (int *)data;
|
|
|
|
|
|
|
|
mac_veriexec_debug++;
|
|
|
|
if (ip) {
|
|
|
|
if (*ip > 0)
|
|
|
|
mac_veriexec_debug = *ip;
|
|
|
|
*ip = mac_veriexec_debug;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mtx_unlock(&ve_mutex);
|
|
|
|
break;
|
|
|
|
case VERIEXEC_DEBUG_OFF:
|
|
|
|
mac_veriexec_debug = 0;
|
|
|
|
break;
|
|
|
|
case VERIEXEC_ENFORCE:
|
|
|
|
mtx_lock(&ve_mutex);
|
|
|
|
if (mac_veriexec_in_state(VERIEXEC_STATE_LOADED))
|
|
|
|
mac_veriexec_set_state(VERIEXEC_STATE_ACTIVE |
|
|
|
|
VERIEXEC_STATE_ENFORCE);
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
mtx_unlock(&ve_mutex);
|
|
|
|
break;
|
2019-05-17 18:25:53 +00:00
|
|
|
case VERIEXEC_GETVERSION:
|
|
|
|
{
|
|
|
|
int *ip = (int *)data;
|
|
|
|
|
|
|
|
if (ip)
|
|
|
|
*ip = MAC_VERIEXEC_VERSION;
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
2018-06-20 00:48:46 +00:00
|
|
|
case VERIEXEC_LOCK:
|
|
|
|
mtx_lock(&ve_mutex);
|
|
|
|
mac_veriexec_set_state(VERIEXEC_STATE_LOCKED);
|
|
|
|
mtx_unlock(&ve_mutex);
|
|
|
|
break;
|
|
|
|
case VERIEXEC_LOAD:
|
|
|
|
if (prison0.pr_securelevel > 0)
|
|
|
|
return (EPERM); /* no updates when secure */
|
|
|
|
|
|
|
|
/* FALLTHROUGH */
|
2019-05-17 19:27:07 +00:00
|
|
|
case VERIEXEC_LABEL_LOAD:
|
2018-06-20 00:48:46 +00:00
|
|
|
case VERIEXEC_SIGNED_LOAD:
|
|
|
|
/*
|
|
|
|
* If we use a loader that will only use a
|
|
|
|
* digitally signed hash list - which it verifies.
|
|
|
|
* We can load fingerprints provided veriexec is not locked.
|
|
|
|
*/
|
|
|
|
if (prison0.pr_securelevel > 0 &&
|
|
|
|
!mac_veriexec_in_state(VERIEXEC_STATE_LOADED)) {
|
|
|
|
/*
|
|
|
|
* If securelevel has been raised and we
|
|
|
|
* do not have any fingerprints loaded,
|
|
|
|
* it would dangerous to do so now.
|
|
|
|
*/
|
|
|
|
return (EPERM);
|
|
|
|
}
|
|
|
|
if (mac_veriexec_in_state(VERIEXEC_STATE_LOCKED))
|
|
|
|
error = EPERM;
|
|
|
|
else {
|
2019-05-17 19:27:07 +00:00
|
|
|
size_t labellen = 0;
|
2018-06-20 00:48:46 +00:00
|
|
|
int flags = FREAD;
|
2019-05-17 19:27:07 +00:00
|
|
|
int override = (cmd != VERIEXEC_LOAD);
|
2018-06-20 00:48:46 +00:00
|
|
|
|
2023-04-02 21:58:27 +00:00
|
|
|
if (params->flags & VERIEXEC_LABEL) {
|
|
|
|
labellen = strnlen(lparams->label,
|
|
|
|
MAXLABELLEN) + 1;
|
|
|
|
if (labellen > MAXLABELLEN)
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
2018-06-20 00:48:46 +00:00
|
|
|
/*
|
|
|
|
* Get the attributes for the file name passed
|
|
|
|
* stash the file's device id and inode number
|
|
|
|
* along with it's fingerprint in a list for
|
|
|
|
* exec to use later.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* FreeBSD seems to copy the args to kernel space
|
|
|
|
*/
|
2021-11-25 21:43:06 +00:00
|
|
|
NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file);
|
2018-06-20 00:48:46 +00:00
|
|
|
if ((error = vn_open(&nid, &flags, 0, NULL)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
error = VOP_GETATTR(nid.ni_vp, &vattr, td->td_ucred);
|
|
|
|
if (error != 0) {
|
|
|
|
mac_veriexec_set_fingerprint_status(nid.ni_vp,
|
|
|
|
FINGERPRINT_INVALID);
|
2020-01-03 22:29:58 +00:00
|
|
|
VOP_UNLOCK(nid.ni_vp);
|
2018-06-20 00:48:46 +00:00
|
|
|
(void) vn_close(nid.ni_vp, FREAD, td->td_ucred,
|
|
|
|
td);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
if (override) {
|
|
|
|
/*
|
|
|
|
* If the file is on a "verified" filesystem
|
|
|
|
* someone may be playing games.
|
|
|
|
*/
|
|
|
|
if ((nid.ni_vp->v_mount->mnt_flag &
|
|
|
|
MNT_VERIFIED) != 0)
|
|
|
|
override = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* invalidate the node fingerprint status
|
|
|
|
* which will have been set in the vn_open
|
|
|
|
* and would always be FINGERPRINT_NOTFOUND
|
|
|
|
*/
|
|
|
|
mac_veriexec_set_fingerprint_status(nid.ni_vp,
|
|
|
|
FINGERPRINT_INVALID);
|
2020-01-03 22:29:58 +00:00
|
|
|
VOP_UNLOCK(nid.ni_vp);
|
2018-06-20 00:48:46 +00:00
|
|
|
(void) vn_close(nid.ni_vp, FREAD, td->td_ucred, td);
|
|
|
|
|
|
|
|
mtx_lock(&ve_mutex);
|
|
|
|
error = mac_veriexec_metadata_add_file(
|
|
|
|
((params->flags & VERIEXEC_FILE) != 0),
|
|
|
|
vattr.va_fsid, vattr.va_fileid, vattr.va_gen,
|
2019-05-17 19:27:07 +00:00
|
|
|
params->fingerprint,
|
|
|
|
(params->flags & VERIEXEC_LABEL) ?
|
|
|
|
lparams->label : NULL, labellen,
|
|
|
|
params->flags, params->fp_type, override);
|
2018-06-20 00:48:46 +00:00
|
|
|
|
|
|
|
mac_veriexec_set_state(VERIEXEC_STATE_LOADED);
|
|
|
|
mtx_unlock(&ve_mutex);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = ENODEV;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cdevsw veriexec_cdevsw = {
|
|
|
|
.d_version = D_VERSION,
|
|
|
|
.d_ioctl = verifiedexecioctl,
|
|
|
|
.d_name = "veriexec",
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
veriexec_drvinit(void *unused __unused)
|
|
|
|
{
|
|
|
|
|
|
|
|
make_dev(&veriexec_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "veriexec");
|
|
|
|
}
|
|
|
|
|
|
|
|
SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
|
2021-07-29 09:02:43 +00:00
|
|
|
MODULE_DEPEND(veriexec, mac_veriexec, MAC_VERIEXEC_VERSION,
|
|
|
|
MAC_VERIEXEC_VERSION, MAC_VERIEXEC_VERSION);
|