1/*
2 * $FreeBSD$
3 *
4 * Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019 Juniper Networks, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30
31#include "opt_capsicum.h"
32#include "opt_mac.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/capsicum.h>
37#include <sys/eventhandler.h>
38#include <sys/fcntl.h>
39#include <sys/file.h>
40#include <sys/filedesc.h>
41#include <sys/imgact.h>
42#include <sys/jail.h>
43#include <sys/kernel.h>
44#include <sys/mac.h>
45#include <sys/mount.h>
46#include <sys/namei.h>
47#include <sys/priv.h>
48#include <sys/proc.h>
49#include <sys/sbuf.h>
50#include <sys/stat.h>
51#include <sys/sysctl.h>
52#include <sys/vnode.h>
53#include <fs/nullfs/null.h>
54#include <security/mac/mac_policy.h>
55
56#include "mac_veriexec.h"
57#include "mac_veriexec_internal.h"
58
59#define	SLOT(l) \
60	mac_label_get((l), mac_veriexec_slot)
61#define	SLOT_SET(l, v) \
62	mac_label_set((l), mac_veriexec_slot, (v))
63
64#ifdef MAC_DEBUG
65#define	MAC_VERIEXEC_DBG(_lvl, _fmt, ...)				\
66	do {								\
67		VERIEXEC_DEBUG((_lvl), (MAC_VERIEXEC_FULLNAME ": " _fmt	\
68		     "\n", ##__VA_ARGS__));				\
69	} while(0)
70#else
71#define	MAC_VERIEXEC_DBG(_lvl, _fmt, ...)
72#endif
73
74static int sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS);
75static int sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS);
76
77SYSCTL_DECL(_security_mac);
78
79SYSCTL_NODE(_security_mac, OID_AUTO, veriexec, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
80    "MAC/veriexec policy controls");
81
82int	mac_veriexec_debug;
83SYSCTL_INT(_security_mac_veriexec, OID_AUTO, debug, CTLFLAG_RW,
84    &mac_veriexec_debug, 0, "Debug level");
85
86static int	mac_veriexec_state;
87SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, state,
88    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
89    0, 0, sysctl_mac_veriexec_state, "A",
90    "Verified execution subsystem state");
91
92SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, db,
93    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_NEEDGIANT,
94    0, 0, sysctl_mac_veriexec_db,
95    "A", "Verified execution fingerprint database");
96
97static int mac_veriexec_slot;
98
99MALLOC_DEFINE(M_VERIEXEC, "veriexec", "Verified execution data");
100
101/**
102 * @internal
103 * @brief Handler for security.mac.veriexec.db sysctl
104 *
105 * Display a human-readable form of the current fingerprint database.
106 */
107static int
108sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS)
109{
110	struct sbuf sb;
111	int error;
112
113	error = sysctl_wire_old_buffer(req, 0);
114	if (error != 0)
115		return (error);
116
117	sbuf_new_for_sysctl(&sb, NULL, 1024, req);
118	mac_veriexec_metadata_print_db(&sb);
119	error = sbuf_finish(&sb);
120	sbuf_delete(&sb);
121
122	return (error);
123}
124
125/**
126 * @internal
127 * @brief Generate human-readable output about the current verified execution
128 *        state.
129 *
130 * @param sbp		sbuf to write output to
131 */
132static void
133mac_veriexec_print_state(struct sbuf *sbp)
134{
135
136	if (mac_veriexec_state & VERIEXEC_STATE_INACTIVE)
137		sbuf_printf(sbp, "inactive ");
138	if (mac_veriexec_state & VERIEXEC_STATE_LOADED)
139		sbuf_printf(sbp, "loaded ");
140	if (mac_veriexec_state & VERIEXEC_STATE_ACTIVE)
141		sbuf_printf(sbp, "active ");
142	if (mac_veriexec_state & VERIEXEC_STATE_ENFORCE)
143		sbuf_printf(sbp, "enforce ");
144	if (mac_veriexec_state & VERIEXEC_STATE_LOCKED)
145		sbuf_printf(sbp, "locked ");
146	if (mac_veriexec_state != 0)
147		sbuf_trim(sbp);
148}
149
150/**
151 * @internal
152 * @brief Handler for security.mac.veriexec.state sysctl
153 *
154 * Display a human-readable form of the current verified execution subsystem
155 * state.
156 */
157static int
158sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS)
159{
160	struct sbuf sb;
161	int error;
162
163	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
164	mac_veriexec_print_state(&sb);
165	sbuf_finish(&sb);
166
167	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
168	sbuf_delete(&sb);
169	return (error);
170}
171
172/**
173 * @internal
174 * @brief Event handler called when a virtual file system is mounted.
175 *
176 * We need to record the file system identifier in the MAC per-policy slot
177 * assigned to veriexec, so we have a key to use in order to reference the
178 * mount point in the meta-data store.
179 *
180 * @param arg		unused argument
181 * @param mp		mount point that is being mounted
182 * @param fsrootvp	vnode of the file system root
183 * @param td		calling thread
184 */
185static void
186mac_veriexec_vfs_mounted(void *arg __unused, struct mount *mp,
187    struct vnode *fsrootvp, struct thread *td)
188{
189	struct vattr va;
190	int error;
191
192	error = VOP_GETATTR(fsrootvp, &va, td->td_ucred);
193	if (error)
194		return;
195
196	SLOT_SET(mp->mnt_label, va.va_fsid);
197#ifdef MAC_DEBUG
198	MAC_VERIEXEC_DBG(3, "set fsid to %ju for mount %p",
199	    (uintmax_t)va.va_fsid, mp);
200#endif
201}
202
203/**
204 * @internal
205 * @brief Event handler called when a virtual file system is unmounted.
206 *
207 * If we recorded a file system identifier in the MAC per-policy slot assigned
208 * to veriexec, then we need to tell the meta-data store to clean up.
209 *
210 * @param arg		unused argument
211 * @param mp		mount point that is being unmounted
212 * @param td		calling thread
213 */
214static void
215mac_veriexec_vfs_unmounted(void *arg __unused, struct mount *mp,
216    struct thread *td)
217{
218	dev_t fsid;
219
220	fsid = SLOT(mp->mnt_label);
221	if (fsid) {
222		MAC_VERIEXEC_DBG(3, "fsid %ju, cleaning up mount",
223		    (uintmax_t)fsid);
224		mac_veriexec_metadata_unmounted(fsid, td);
225	}
226}
227
228/**
229 * @internal
230 * @brief The mount point is being initialized, set the value in the MAC
231 *     per-policy slot for veriexec to zero.
232 *
233 * @note A value of zero in this slot indicates no file system identifier
234 *     is assigned.
235 *
236 * @param label the label that is being initialized
237 */
238static void
239mac_veriexec_mount_init_label(struct label *label)
240{
241
242	SLOT_SET(label, 0);
243}
244
245/**
246 * @internal
247 * @brief The mount-point is being destroyed, reset the value in the MAC
248 *     per-policy slot for veriexec back to zero.
249 *
250 * @note A value of zero in this slot indicates no file system identifier
251 *     is assigned.
252 *
253 * @param label the label that is being destroyed
254 */
255static void
256mac_veriexec_mount_destroy_label(struct label *label)
257{
258
259	SLOT_SET(label, 0);
260}
261
262/**
263 * @internal
264 * @brief The vnode label is being initialized, set the value in the MAC
265 *     per-policy slot for veriexec to @c FINGERPRINT_INVALID
266 *
267 * @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
268 *
269 * @param label		the label that is being initialized
270 */
271static void
272mac_veriexec_vnode_init_label(struct label *label)
273{
274
275	SLOT_SET(label, FINGERPRINT_INVALID);
276}
277
278/**
279 * @internal
280 * @brief The vnode label is being destroyed, reset the value in the MAC
281 *        per-policy slot for veriexec back to @c FINGERPRINT_INVALID
282 *
283 * @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
284 *
285 * @param label		the label that is being destroyed
286 */
287static void
288mac_veriexec_vnode_destroy_label(struct label *label)
289{
290
291	SLOT_SET(label, FINGERPRINT_INVALID);
292}
293
294/**
295 * @internal
296 * @brief Copy the value in the MAC per-policy slot assigned to veriexec from
297 *        the @p src label to the @p dest label
298 */
299static void
300mac_veriexec_copy_label(struct label *src, struct label *dest)
301{
302
303	SLOT_SET(dest, SLOT(src));
304}
305
306/**
307 * @internal
308 * @brief Check if the requested process can be debugged
309 *
310 * @param cred		credentials to use
311 * @param p		process to debug
312 *
313 * @return 0 if debugging is allowed, otherwise an error code.
314 */
315static int
316mac_veriexec_proc_check_debug(struct ucred *cred, struct proc *p)
317{
318	int error, flags;
319
320	/* If we are not enforcing veriexec, nothing for us to check */
321	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
322		return (0);
323
324	error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
325	if (error != 0)
326		return (0);
327
328	return ((flags & VERIEXEC_NOTRACE) ? EACCES : 0);
329}
330
331/**
332 * @internal
333 * @brief A KLD load has been requested and needs to be validated.
334 *
335 * @param cred		credentials to use
336 * @param vp		vnode of the KLD that has been requested
337 * @param vlabel	vnode label assigned to the vnode
338 *
339 * @return 0 if the KLD load is allowed, otherwise an error code.
340 */
341static int
342mac_veriexec_kld_check_load(struct ucred *cred, struct vnode *vp,
343    struct label *vlabel)
344{
345	struct vattr va;
346	struct thread *td = curthread;
347	fingerprint_status_t status;
348	int error;
349
350	/*
351	 * If we are not actively enforcing, allow it
352	 */
353	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
354		return (0);
355
356	/* Get vnode attributes */
357	error = VOP_GETATTR(vp, &va, cred);
358	if (error)
359		return (error);
360
361	/*
362	 * Fetch the fingerprint status for the vnode
363	 * (starting with files first)
364	 */
365	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
366	    VERIEXEC_FILES_FIRST);
367	if (error && error != EAUTH)
368		return (error);
369
370	/*
371	 * By now we should have status...
372	 */
373	status = mac_veriexec_get_fingerprint_status(vp);
374	switch (status) {
375	case FINGERPRINT_FILE:
376	case FINGERPRINT_VALID:
377	case FINGERPRINT_INDIRECT:
378		if (error)
379			return (error);
380		break;
381	default:
382		/*
383		 * kldload should fail unless there is a valid fingerprint
384		 * registered.
385		 */
386		MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev %ju, "
387		    "file %ju.%ju\n", status, (uintmax_t)va.va_fsid,
388		    (uintmax_t)va.va_fileid, (uintmax_t)va.va_gen);
389		return (EAUTH);
390	}
391
392	/* Everything is good, allow the KLD to be loaded */
393	return (0);
394}
395
396/**
397 * @internal
398 * @brief Check privileges that veriexec needs to be concerned about.
399 *
400 * The following privileges are checked by this function:
401 *  - PRIV_KMEM_WRITE\n
402 *    Check if writes to /dev/mem and /dev/kmem are allowed\n
403 *    (Only trusted processes are allowed)
404 *
405 * @param cred		credentials to use
406 * @param priv		privilege to check
407 *
408 * @return 0 if the privilege is allowed, error code otherwise.
409 */
410static int
411mac_veriexec_priv_check(struct ucred *cred, int priv)
412{
413
414	/* If we are not enforcing veriexec, nothing for us to check */
415	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
416		return (0);
417
418	switch (priv) {
419	case PRIV_KMEM_WRITE:
420		if (!mac_veriexec_proc_is_trusted(cred, curproc))
421			return (EPERM);
422		break;
423	default:
424		break;
425	}
426	return (0);
427}
428
429static int
430mac_veriexec_sysctl_check(struct ucred *cred, struct sysctl_oid *oidp,
431    void *arg1, int arg2, struct sysctl_req *req)
432{
433	struct sysctl_oid *oid;
434
435	/* If we are not enforcing veriexec, nothing for us to check */
436	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
437		return (0);
438
439	oid = oidp;
440	if (oid->oid_kind & CTLFLAG_SECURE) {
441		return (EPERM);		/* XXX call mac_veriexec_priv_check? */
442	}
443	return 0;
444}
445
446/**
447 * @internal
448 * @brief A program is being executed and needs to be validated.
449 *
450 * @param cred		credentials to use
451 * @param vp		vnode of the program that is being executed
452 * @param label		vnode label assigned to the vnode
453 * @param imgp		parameters for the image to be executed
454 * @param execlabel	optional exec label
455 *
456 * @return 0 if the program should be allowed to execute, otherwise an error
457 *     code.
458 */
459static int
460mac_veriexec_vnode_check_exec(struct ucred *cred __unused,
461    struct vnode *vp __unused, struct label *label __unused,
462    struct image_params *imgp, struct label *execlabel __unused)
463{
464	struct thread *td = curthread;
465	int error;
466
467	error = mac_veriexec_fingerprint_check_image(imgp, 0, td);
468	return (error);
469}
470
471/**
472 * @brief Check fingerprint for the specified vnode and validate it
473 *
474 * @param cred		credentials to use
475 * @param vp		vnode of the file
476 * @param accmode	access mode to check (read, write, append, create,
477 *			verify, etc.)
478 *
479 * @return 0 if the file validated, otherwise an error code.
480 */
481static int
482mac_veriexec_check_vp(struct ucred *cred, struct vnode *vp, accmode_t accmode)
483{
484	struct vattr va;
485	struct thread *td = curthread;
486	fingerprint_status_t status;
487	int error;
488
489	/* Get vnode attributes */
490	error = VOP_GETATTR(vp, &va, cred);
491	if (error)
492		return (error);
493
494	/* Get the fingerprint status for the file */
495	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
496	    VERIEXEC_FILES_FIRST);
497	if (error && error != EAUTH)
498		return (error);
499
500	/*
501	 * By now we should have status...
502	 */
503	status = mac_veriexec_get_fingerprint_status(vp);
504	if (accmode & VWRITE) {
505		/*
506		 * If file has a fingerprint then deny the write request,
507		 * otherwise invalidate the status so we don't keep checking
508		 * for the file having a fingerprint.
509		 */
510		switch (status) {
511		case FINGERPRINT_FILE:
512		case FINGERPRINT_VALID:
513		case FINGERPRINT_INDIRECT:
514			MAC_VERIEXEC_DBG(2,
515			    "attempted write to fingerprinted file for dev "
516			    "%ju, file %ju.%ju\n", (uintmax_t)va.va_fsid,
517			    (uintmax_t)va.va_fileid, (uintmax_t)va.va_gen);
518			return (EPERM);
519		default:
520			break;
521		}
522	}
523	if (accmode & VVERIFY) {
524		switch (status) {
525		case FINGERPRINT_FILE:
526		case FINGERPRINT_VALID:
527		case FINGERPRINT_INDIRECT:
528			if (error)
529				return (error);
530			break;
531		default:
532			/*
533			 * Caller wants open to fail unless there is a valid
534			 * fingerprint registered.
535			 */
536			MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev "
537			    "%ju, file %ju.%ju\n", status,
538			    (uintmax_t)va.va_fsid, (uintmax_t)va.va_fileid,
539			    (uintmax_t)va.va_gen);
540			return (EAUTH);
541		}
542	}
543	return (0);
544}
545
546/**
547 * @brief Opening a file has been requested and may need to be validated.
548 *
549 * @param cred		credentials to use
550 * @param vp		vnode of the file to open
551 * @param label		vnode label assigned to the vnode
552 * @param accmode	access mode to use for opening the file (read, write,
553 * 			append, create, verify, etc.)
554 *
555 * @return 0 if opening the file should be allowed, otherwise an error code.
556 */
557static int
558mac_veriexec_vnode_check_open(struct ucred *cred, struct vnode *vp,
559	struct label *label __unused, accmode_t accmode)
560{
561	int error;
562
563	/*
564	 * Look for the file on the fingerprint lists iff it has not been seen
565	 * before.
566	 */
567	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
568		return (0);
569
570	error = mac_veriexec_check_vp(cred, vp, accmode);
571	return (error);
572}
573
574/**
575 * @brief Check mode changes on file to ensure they should be allowed.
576 *
577 * We cannot allow chmod of SUID or SGID on verified files.
578 *
579 * @param cred		credentials to use
580 * @param vp		vnode of the file to open
581 * @param label		vnode label assigned to the vnode
582 * @param mode		mode flags to set
583 *
584 * @return 0 if the mode change should be allowed, EAUTH otherwise.
585 */
586static int
587mac_veriexec_vnode_check_setmode(struct ucred *cred, struct vnode *vp,
588    struct label *label __unused, mode_t mode)
589{
590	int error;
591
592	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
593		return (0);
594
595	/*
596	 * Do not allow chmod (set-[gu]id) of verified file
597	 */
598	error = mac_veriexec_check_vp(cred, vp, VVERIFY);
599	if (error == EAUTH)             /* it isn't verified */
600		return (0);
601	if (error == 0 && (mode & (S_ISUID|S_ISGID)) != 0)
602		return (EAUTH);
603	return (0);
604}
605
606/**
607 * @internal
608 * @brief Initialize the mac_veriexec MAC policy
609 *
610 * @param mpc		MAC policy configuration
611 */
612static void
613mac_veriexec_init(struct mac_policy_conf *mpc __unused)
614{
615	/* Initialize state */
616	mac_veriexec_state = VERIEXEC_STATE_INACTIVE;
617
618	/* Initialize meta-data storage */
619	mac_veriexec_metadata_init();
620
621	/* Initialize fingerprint ops */
622	mac_veriexec_fingerprint_init();
623
624	/* Register event handlers */
625	EVENTHANDLER_REGISTER(vfs_mounted, mac_veriexec_vfs_mounted, NULL,
626	    EVENTHANDLER_PRI_FIRST);
627	EVENTHANDLER_REGISTER(vfs_unmounted, mac_veriexec_vfs_unmounted, NULL,
628	    EVENTHANDLER_PRI_LAST);
629}
630
631/**
632 * @internal
633 * @brief MAC policy-specific syscall for mac_veriexec
634 *
635 * The following syscalls are implemented:
636 *   - @c MAC_VERIEXEC_CHECK_SYSCALL
637 *        Check if the file referenced by a file descriptor has a fingerprint
638 *        registered in the meta-data store.
639 *
640 * @param td		calling thread
641 * @param call		system call number
642 * @param arg		arugments to the syscall
643 *
644 * @return 0 on success, otherwise an error code.
645 */
646static int
647mac_veriexec_syscall(struct thread *td, int call, void *arg)
648{
649	struct image_params img;
650	struct nameidata nd;
651	cap_rights_t rights;
652	struct vattr va;
653	struct file *fp;
654	int error;
655
656	switch (call) {
657	case MAC_VERIEXEC_CHECK_FD_SYSCALL:
658		/* Get the vnode associated with the file descriptor passed */
659		error = getvnode(td, (uintptr_t) arg,
660		    cap_rights_init_one(&rights, CAP_READ), &fp);
661		if (error)
662			return (error);
663		if (fp->f_type != DTYPE_VNODE) {
664			MAC_VERIEXEC_DBG(3, "MAC_VERIEXEC_CHECK_SYSCALL: "
665			    "file is not vnode type (type=0x%x)",
666			    fp->f_type);
667			error = EINVAL;
668			goto cleanup_file;
669		}
670
671		/*
672		 * setup the bits of image_params that are used by
673		 * mac_veriexec_check_fingerprint().
674		 */
675		bzero(&img, sizeof(img));
676		img.proc = td->td_proc;
677		img.vp = fp->f_vnode;
678		img.attr = &va;
679
680		/*
681		 * Get vnode attributes
682		 * (need to obtain a lock on the vnode first)
683		 */
684		vn_lock(img.vp, LK_EXCLUSIVE | LK_RETRY);
685		error = VOP_GETATTR(fp->f_vnode, &va,  td->td_ucred);
686		if (error)
687			goto check_done;
688
689		MAC_VERIEXEC_DBG(2, "mac_veriexec_fingerprint_check_image: "
690		    "va_mode=%o, check_files=%d\n", va.va_mode,
691		    ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0));
692		error = mac_veriexec_fingerprint_check_image(&img,
693		    ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0), td);
694check_done:
695		/* Release the lock we obtained earlier */
696		VOP_UNLOCK(img.vp);
697cleanup_file:
698		fdrop(fp, td);
699		break;
700	case MAC_VERIEXEC_CHECK_PATH_SYSCALL:
701		/* Look up the path to get the vnode */
702		NDINIT(&nd, LOOKUP,
703		    FOLLOW | LOCKLEAF | LOCKSHARED | AUDITVNODE1,
704		    UIO_USERSPACE, arg, td);
705		error = namei(&nd);
706		if (error != 0)
707			break;
708		NDFREE(&nd, NDF_ONLY_PNBUF);
709
710		/* Check the fingerprint status of the vnode */
711		error = mac_veriexec_check_vp(td->td_ucred, nd.ni_vp, VVERIFY);
712		vput(nd.ni_vp);
713		break;
714	default:
715		error = EOPNOTSUPP;
716	}
717	return (error);
718}
719
720static struct mac_policy_ops mac_veriexec_ops =
721{
722	.mpo_init = mac_veriexec_init,
723	.mpo_kld_check_load = mac_veriexec_kld_check_load,
724	.mpo_mount_destroy_label = mac_veriexec_mount_destroy_label,
725	.mpo_mount_init_label = mac_veriexec_mount_init_label,
726	.mpo_priv_check = mac_veriexec_priv_check,
727	.mpo_proc_check_debug = mac_veriexec_proc_check_debug,
728	.mpo_syscall = mac_veriexec_syscall,
729	.mpo_system_check_sysctl = mac_veriexec_sysctl_check,
730	.mpo_vnode_check_exec = mac_veriexec_vnode_check_exec,
731	.mpo_vnode_check_open = mac_veriexec_vnode_check_open,
732	.mpo_vnode_check_setmode = mac_veriexec_vnode_check_setmode,
733	.mpo_vnode_copy_label = mac_veriexec_copy_label,
734	.mpo_vnode_destroy_label = mac_veriexec_vnode_destroy_label,
735	.mpo_vnode_init_label = mac_veriexec_vnode_init_label,
736};
737
738MAC_POLICY_SET(&mac_veriexec_ops, mac_veriexec, MAC_VERIEXEC_FULLNAME,
739    MPC_LOADTIME_FLAG_NOTLATE, &mac_veriexec_slot);
740MODULE_VERSION(mac_veriexec, 1);
741
742static struct vnode *
743mac_veriexec_bottom_vnode(struct vnode *vp)
744{
745	struct vnode *ldvp = NULL;
746
747	/*
748	 * XXX This code is bogus. nullfs is not the only stacking
749	 * filesystem. Less bogus code would add a VOP to reach bottom
750	 * vnode and would not make assumptions how to get there.
751	 */
752	if (vp->v_mount != NULL &&
753	    strcmp(vp->v_mount->mnt_vfc->vfc_name, "nullfs") == 0)
754		ldvp = NULLVPTOLOWERVP(vp);
755	return (ldvp);
756}
757
758/**
759 * @brief Get the fingerprint status set on a vnode.
760 *
761 * @param vp		vnode to obtain fingerprint status from
762 *
763 * @return Fingerprint status assigned to the vnode.
764 */
765fingerprint_status_t
766mac_veriexec_get_fingerprint_status(struct vnode *vp)
767{
768	fingerprint_status_t fps;
769	struct vnode *ldvp;
770
771	fps = SLOT(vp->v_label);
772	switch (fps) {
773	case FINGERPRINT_VALID:
774	case FINGERPRINT_INDIRECT:
775	case FINGERPRINT_FILE:
776		break;
777	default:
778		/* we may need to recurse */
779		ldvp = mac_veriexec_bottom_vnode(vp);
780		if (ldvp != NULL)
781			return mac_veriexec_get_fingerprint_status(ldvp);
782		break;
783	}
784	return fps;
785}
786
787/**
788 * @brief Get the current verified execution subsystem state.
789 *
790 * @return Current set of verified execution subsystem state flags.
791 */
792int
793mac_veriexec_get_state(void)
794{
795
796	return (mac_veriexec_state);
797}
798
799/**
800 * @brief Determine if the verified execution subsystem state has specific
801 *     flags set.
802 *
803 * @param state		mask of flags to check
804 *
805 * @return State flags set within the masked bits
806 */
807int
808mac_veriexec_in_state(int state)
809{
810
811	return (mac_veriexec_state & state);
812}
813
814/**
815 * @brief Set the fingerprint status for a vnode
816 *
817 * Fingerprint status is stored in the MAC per-policy slot assigned to
818 * mac_veriexec.
819 *
820 * @param vp		vnode to store the fingerprint status on
821 * @param fp_status	fingerprint status to store
822 */
823void
824mac_veriexec_set_fingerprint_status(struct vnode *vp,
825    fingerprint_status_t fp_status)
826{
827	struct vnode *ldvp;
828
829	/* recurse until we find the real storage */
830	ldvp = mac_veriexec_bottom_vnode(vp);
831	if (ldvp != NULL) {
832		mac_veriexec_set_fingerprint_status(ldvp, fp_status);
833		return;
834	}
835	SLOT_SET(vp->v_label, fp_status);
836}
837
838/**
839 * @brief Set verified execution subsystem state flags
840 *
841 * @note Flags can only be added to the current state, not removed.
842 *
843 * @param state		state flags to add to the current state
844 */
845void
846mac_veriexec_set_state(int state)
847{
848
849	mac_veriexec_state |= state;
850}
851
852/**
853 * @brief Determine if the process is trusted
854 *
855 * @param cred		credentials to use
856 * @param p		the process in question
857 *
858 * @return 1 if the process is trusted, otherwise 0.
859 */
860int
861mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p)
862{
863	int already_locked, error, flags;
864
865	/* Make sure we lock the process if we do not already have the lock */
866	already_locked = PROC_LOCKED(p);
867	if (!already_locked)
868		PROC_LOCK(p);
869
870	error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
871
872	/* Unlock the process if we locked it previously */
873	if (!already_locked)
874		PROC_UNLOCK(p);
875
876	/* Any errors, deny access */
877	if (error != 0)
878		return (0);
879
880	/* Check that the trusted flag is set */
881	return ((flags & VERIEXEC_TRUSTED) == VERIEXEC_TRUSTED);
882}
883