xref: /freebsd/sys/security/mac_veriexec/mac_veriexec.c (revision 8512d82ea0700df1c31232a0fe4c777d95600de3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011-2023 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_framework.h>
55 #include <security/mac/mac_policy.h>
56 
57 #include "mac_veriexec.h"
58 #include "mac_veriexec_internal.h"
59 
60 #define	SLOT(l) \
61 	mac_label_get((l), mac_veriexec_slot)
62 #define	SLOT_SET(l, v) \
63 	mac_label_set((l), mac_veriexec_slot, (v))
64 
65 #ifdef MAC_DEBUG
66 #define	MAC_VERIEXEC_DBG(_lvl, _fmt, ...)				\
67 	do {								\
68 		VERIEXEC_DEBUG((_lvl), (MAC_VERIEXEC_FULLNAME ": " _fmt	\
69 		     "\n", ##__VA_ARGS__));				\
70 	} while(0)
71 #else
72 #define	MAC_VERIEXEC_DBG(_lvl, _fmt, ...)
73 #endif
74 
75 static int sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS);
76 static int sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS);
77 static struct mac_policy_ops mac_veriexec_ops;
78 
79 SYSCTL_DECL(_security_mac);
80 
81 SYSCTL_NODE(_security_mac, OID_AUTO, veriexec, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
82     "MAC/veriexec policy controls");
83 
84 int	mac_veriexec_debug;
85 SYSCTL_INT(_security_mac_veriexec, OID_AUTO, debug, CTLFLAG_RW,
86     &mac_veriexec_debug, 0, "Debug level");
87 
88 static int	mac_veriexec_state;
89 SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, state,
90     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
91     0, 0, sysctl_mac_veriexec_state, "A",
92     "Verified execution subsystem state");
93 
94 SYSCTL_PROC(_security_mac_veriexec, OID_AUTO, db,
95     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_NEEDGIANT,
96     0, 0, sysctl_mac_veriexec_db,
97     "A", "Verified execution fingerprint database");
98 
99 
100 static int mac_veriexec_slot;
101 
102 static int mac_veriexec_block_unlink;
103 
104 MALLOC_DEFINE(M_VERIEXEC, "veriexec", "Verified execution data");
105 
106 /**
107  * @internal
108  * @brief Handler for security.mac.veriexec.db sysctl
109  *
110  * Display a human-readable form of the current fingerprint database.
111  */
112 static int
113 sysctl_mac_veriexec_db(SYSCTL_HANDLER_ARGS)
114 {
115 	struct sbuf sb;
116 	int error;
117 
118 	error = sysctl_wire_old_buffer(req, 0);
119 	if (error != 0)
120 		return (error);
121 
122 	sbuf_new_for_sysctl(&sb, NULL, 1024, req);
123 	mac_veriexec_metadata_print_db(&sb);
124 	error = sbuf_finish(&sb);
125 	sbuf_delete(&sb);
126 
127 	return (error);
128 }
129 
130 /**
131  * @internal
132  * @brief Generate human-readable output about the current verified execution
133  *        state.
134  *
135  * @param sbp		sbuf to write output to
136  */
137 static void
138 mac_veriexec_print_state(struct sbuf *sbp)
139 {
140 
141 	if (mac_veriexec_state & VERIEXEC_STATE_INACTIVE)
142 		sbuf_printf(sbp, "inactive ");
143 	if (mac_veriexec_state & VERIEXEC_STATE_LOADED)
144 		sbuf_printf(sbp, "loaded ");
145 	if (mac_veriexec_state & VERIEXEC_STATE_ACTIVE)
146 		sbuf_printf(sbp, "active ");
147 	if (mac_veriexec_state & VERIEXEC_STATE_ENFORCE)
148 		sbuf_printf(sbp, "enforce ");
149 	if (mac_veriexec_state & VERIEXEC_STATE_LOCKED)
150 		sbuf_printf(sbp, "locked ");
151 	if (mac_veriexec_state != 0)
152 		sbuf_trim(sbp);
153 }
154 
155 /**
156  * @internal
157  * @brief Handler for security.mac.veriexec.state sysctl
158  *
159  * Display a human-readable form of the current verified execution subsystem
160  * state.
161  */
162 static int
163 sysctl_mac_veriexec_state(SYSCTL_HANDLER_ARGS)
164 {
165 	struct sbuf sb;
166 	int error;
167 
168 	sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND);
169 	mac_veriexec_print_state(&sb);
170 	sbuf_finish(&sb);
171 
172 	error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
173 	sbuf_delete(&sb);
174 	return (error);
175 }
176 
177 /**
178  * @internal
179  * @brief Event handler called when a virtual file system is mounted.
180  *
181  * We need to record the file system identifier in the MAC per-policy slot
182  * assigned to veriexec, so we have a key to use in order to reference the
183  * mount point in the meta-data store.
184  *
185  * @param arg		unused argument
186  * @param mp		mount point that is being mounted
187  * @param fsrootvp	vnode of the file system root
188  * @param td		calling thread
189  */
190 static void
191 mac_veriexec_vfs_mounted(void *arg __unused, struct mount *mp,
192     struct vnode *fsrootvp, struct thread *td)
193 {
194 	struct vattr va;
195 	int error;
196 
197 	error = VOP_GETATTR(fsrootvp, &va, td->td_ucred);
198 	if (error)
199 		return;
200 
201 	SLOT_SET(mp->mnt_label, va.va_fsid);
202 #ifdef MAC_DEBUG
203 	MAC_VERIEXEC_DBG(3, "set fsid to %ju for mount %p",
204 	    (uintmax_t)va.va_fsid, mp);
205 #endif
206 }
207 
208 /**
209  * @internal
210  * @brief Event handler called when a virtual file system is unmounted.
211  *
212  * If we recorded a file system identifier in the MAC per-policy slot assigned
213  * to veriexec, then we need to tell the meta-data store to clean up.
214  *
215  * @param arg		unused argument
216  * @param mp		mount point that is being unmounted
217  * @param td		calling thread
218  */
219 static void
220 mac_veriexec_vfs_unmounted(void *arg __unused, struct mount *mp,
221     struct thread *td)
222 {
223 	dev_t fsid;
224 
225 	fsid = SLOT(mp->mnt_label);
226 	if (fsid) {
227 		MAC_VERIEXEC_DBG(3, "fsid %ju, cleaning up mount",
228 		    (uintmax_t)fsid);
229 		mac_veriexec_metadata_unmounted(fsid, td);
230 	}
231 }
232 
233 /**
234  * @internal
235  * @brief The mount point is being initialized, set the value in the MAC
236  *     per-policy slot for veriexec to zero.
237  *
238  * @note A value of zero in this slot indicates no file system identifier
239  *     is assigned.
240  *
241  * @param label the label that is being initialized
242  */
243 static void
244 mac_veriexec_mount_init_label(struct label *label)
245 {
246 
247 	SLOT_SET(label, 0);
248 }
249 
250 /**
251  * @internal
252  * @brief The mount-point is being destroyed, reset the value in the MAC
253  *     per-policy slot for veriexec back to zero.
254  *
255  * @note A value of zero in this slot indicates no file system identifier
256  *     is assigned.
257  *
258  * @param label the label that is being destroyed
259  */
260 static void
261 mac_veriexec_mount_destroy_label(struct label *label)
262 {
263 
264 	SLOT_SET(label, 0);
265 }
266 
267 /**
268  * @internal
269  * @brief The vnode label is being initialized, set the value in the MAC
270  *     per-policy slot for veriexec to @c FINGERPRINT_INVALID
271  *
272  * @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
273  *
274  * @param label		the label that is being initialized
275  */
276 static void
277 mac_veriexec_vnode_init_label(struct label *label)
278 {
279 
280 	SLOT_SET(label, FINGERPRINT_INVALID);
281 }
282 
283 /**
284  * @internal
285  * @brief The vnode label is being destroyed, reset the value in the MAC
286  *        per-policy slot for veriexec back to @c FINGERPRINT_INVALID
287  *
288  * @note @c FINGERPRINT_INVALID indicates the fingerprint is invalid.
289  *
290  * @param label		the label that is being destroyed
291  */
292 static void
293 mac_veriexec_vnode_destroy_label(struct label *label)
294 {
295 
296 	SLOT_SET(label, FINGERPRINT_INVALID);
297 }
298 
299 /**
300  * @internal
301  * @brief Copy the value in the MAC per-policy slot assigned to veriexec from
302  *        the @p src label to the @p dest label
303  */
304 static void
305 mac_veriexec_copy_label(struct label *src, struct label *dest)
306 {
307 
308 	SLOT_SET(dest, SLOT(src));
309 }
310 
311 /**
312  * @internal
313  * @brief Check if the requested process can be debugged
314  *
315  * @param cred		credentials to use
316  * @param p		process to debug
317  *
318  * @return 0 if debugging is allowed, otherwise an error code.
319  */
320 static int
321 mac_veriexec_proc_check_debug(struct ucred *cred, struct proc *p)
322 {
323 	int error, flags;
324 
325 	/* If we are not enforcing veriexec, nothing for us to check */
326 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
327 		return (0);
328 
329 	error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
330 	if (error != 0)
331 		return (0);
332 
333 	error = (flags & (VERIEXEC_NOTRACE|VERIEXEC_TRUSTED)) ? EACCES : 0;
334 	MAC_VERIEXEC_DBG(4, "%s flags=%#x error=%d", __func__, flags, error);
335 
336 	return (error);
337 }
338 
339 /**
340  * @internal
341  * @brief A KLD load has been requested and needs to be validated.
342  *
343  * @param cred		credentials to use
344  * @param vp		vnode of the KLD that has been requested
345  * @param vlabel	vnode label assigned to the vnode
346  *
347  * @return 0 if the KLD load is allowed, otherwise an error code.
348  */
349 static int
350 mac_veriexec_kld_check_load(struct ucred *cred, struct vnode *vp,
351     struct label *vlabel)
352 {
353 	struct vattr va;
354 	struct thread *td = curthread;
355 	fingerprint_status_t status;
356 	int error;
357 
358 	/*
359 	 * If we are not actively enforcing, allow it
360 	 */
361 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
362 		return (0);
363 
364 	/* Get vnode attributes */
365 	error = VOP_GETATTR(vp, &va, cred);
366 	if (error)
367 		return (error);
368 
369 	/*
370 	 * Fetch the fingerprint status for the vnode
371 	 * (starting with files first)
372 	 */
373 	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
374 	    VERIEXEC_FILES_FIRST);
375 	if (error && error != EAUTH)
376 		return (error);
377 
378 	/*
379 	 * By now we should have status...
380 	 */
381 	status = mac_veriexec_get_fingerprint_status(vp);
382 	switch (status) {
383 	case FINGERPRINT_FILE:
384 	case FINGERPRINT_VALID:
385 	case FINGERPRINT_INDIRECT:
386 		if (error)
387 			return (error);
388 		break;
389 	default:
390 		/*
391 		 * kldload should fail unless there is a valid fingerprint
392 		 * registered.
393 		 */
394 		MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev %ju, "
395 		    "file %ju.%ju\n", status, (uintmax_t)va.va_fsid,
396 		    (uintmax_t)va.va_fileid, (uintmax_t)va.va_gen);
397 		return (EAUTH);
398 	}
399 
400 	/* Everything is good, allow the KLD to be loaded */
401 	return (0);
402 }
403 
404 /**
405  * @internal
406  * @brief Check privileges that veriexec needs to be concerned about.
407  *
408  * The following privileges are checked by this function:
409  *  - PRIV_KMEM_WRITE\n
410  *    Check if writes to /dev/mem and /dev/kmem are allowed\n
411  *    (Only trusted processes are allowed)
412  *  - PRIV_VERIEXEC_CONTROL\n
413  *    Check if manipulating veriexec is allowed\n
414  *    (only trusted processes are allowed)
415  *
416  * @param cred		credentials to use
417  * @param priv		privilege to check
418  *
419  * @return 0 if the privilege is allowed, error code otherwise.
420  */
421 static int
422 mac_veriexec_priv_check(struct ucred *cred, int priv)
423 {
424 	int error;
425 
426 	/* If we are not enforcing veriexec, nothing for us to check */
427 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
428 		return (0);
429 
430 	error = 0;
431 	switch (priv) {
432 	case PRIV_KMEM_WRITE:
433 	case PRIV_VERIEXEC_CONTROL:
434 		/*
435 		 * Do not allow writing to memory or manipulating veriexec,
436 		 * unless trusted
437 		 */
438 		if (mac_veriexec_proc_is_trusted(cred, curproc) == 0 &&
439 		    mac_priv_grant(cred, priv) != 0)
440 			error = EPERM;
441 		MAC_VERIEXEC_DBG(4, "%s priv=%d error=%d", __func__, priv,
442 		    error);
443 		break;
444 	default:
445 		break;
446 	}
447 	return (error);
448 }
449 
450 /**
451  * @internal
452  * @brief Check if the requested sysctl should be allowed
453  *
454  * @param cred         credentials to use
455  * @param oidp         sysctl OID
456  * @param arg1         first sysctl argument
457  * @param arg2         second sysctl argument
458  * @param req          sysctl request information
459  *
460  * @return 0 if the sysctl should be allowed, otherwise an error code.
461  */
462 static int
463 mac_veriexec_sysctl_check(struct ucred *cred, struct sysctl_oid *oidp,
464     void *arg1, int arg2, struct sysctl_req *req)
465 {
466 	struct sysctl_oid *oid;
467 
468 	/* If we are not enforcing veriexec, nothing for us to check */
469 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
470 		return (0);
471 
472 	oid = oidp;
473 	if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) {
474 		return (EPERM);		/* XXX call mac_veriexec_priv_check? */
475 	}
476 	return 0;
477 }
478 
479 /**
480  * @internal
481  * @brief A program is being executed and needs to be validated.
482  *
483  * @param cred		credentials to use
484  * @param vp		vnode of the program that is being executed
485  * @param label		vnode label assigned to the vnode
486  * @param imgp		parameters for the image to be executed
487  * @param execlabel	optional exec label
488  *
489  * @return 0 if the program should be allowed to execute, otherwise an error
490  *     code.
491  */
492 static int
493 mac_veriexec_vnode_check_exec(struct ucred *cred __unused,
494     struct vnode *vp __unused, struct label *label __unused,
495     struct image_params *imgp, struct label *execlabel __unused)
496 {
497 	struct thread *td = curthread;
498 	int error;
499 
500 	error = mac_veriexec_fingerprint_check_image(imgp, 0, td);
501 	return (error);
502 }
503 
504 /**
505  * @brief Check fingerprint for the specified vnode and validate it
506  *
507  * @param cred		credentials to use
508  * @param vp		vnode of the file
509  * @param accmode	access mode to check (read, write, append, create,
510  *			verify, etc.)
511  *
512  * @return 0 if the file validated, otherwise an error code.
513  */
514 static int
515 mac_veriexec_check_vp(struct ucred *cred, struct vnode *vp, accmode_t accmode)
516 {
517 	struct vattr va;
518 	struct thread *td = curthread;
519 	fingerprint_status_t status;
520 	int error;
521 
522 	/* Get vnode attributes */
523 	error = VOP_GETATTR(vp, &va, cred);
524 	if (error)
525 		return (error);
526 
527 	/* Get the fingerprint status for the file */
528 	error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
529 	    VERIEXEC_FILES_FIRST);
530 	if (error && error != EAUTH)
531 		return (error);
532 
533 	/*
534 	 * By now we should have status...
535 	 */
536 	status = mac_veriexec_get_fingerprint_status(vp);
537 	if (accmode & VWRITE) {
538 		/*
539 		 * If file has a fingerprint then deny the write request,
540 		 * otherwise invalidate the status so we don't keep checking
541 		 * for the file having a fingerprint.
542 		 */
543 		switch (status) {
544 		case FINGERPRINT_FILE:
545 		case FINGERPRINT_VALID:
546 		case FINGERPRINT_INDIRECT:
547 			MAC_VERIEXEC_DBG(2,
548 			    "attempted write to fingerprinted file for dev "
549 			    "%ju, file %ju.%ju\n", (uintmax_t)va.va_fsid,
550 			    (uintmax_t)va.va_fileid, (uintmax_t)va.va_gen);
551 			return (EPERM);
552 		default:
553 			break;
554 		}
555 	}
556 	if (accmode & VVERIFY) {
557 		switch (status) {
558 		case FINGERPRINT_FILE:
559 		case FINGERPRINT_VALID:
560 		case FINGERPRINT_INDIRECT:
561 			if (error)
562 				return (error);
563 			break;
564 		default:
565 			/* Allow for overriding verification requirement */
566 			if (mac_priv_grant(cred, PRIV_VERIEXEC_NOVERIFY) == 0)
567 				return (0);
568 			/*
569 			 * Caller wants open to fail unless there is a valid
570 			 * fingerprint registered.
571 			 */
572 			MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev "
573 			    "%ju, file %ju.%ju\n", status,
574 			    (uintmax_t)va.va_fsid, (uintmax_t)va.va_fileid,
575 			    (uintmax_t)va.va_gen);
576 			return (EAUTH);
577 		}
578 	}
579 	return (0);
580 }
581 
582 /**
583  * @brief Opening a file has been requested and may need to be validated.
584  *
585  * @param cred		credentials to use
586  * @param vp		vnode of the file to open
587  * @param label		vnode label assigned to the vnode
588  * @param accmode	access mode to use for opening the file (read, write,
589  * 			append, create, verify, etc.)
590  *
591  * @return 0 if opening the file should be allowed, otherwise an error code.
592  */
593 static int
594 mac_veriexec_vnode_check_open(struct ucred *cred, struct vnode *vp,
595 	struct label *label __unused, accmode_t accmode)
596 {
597 	int error;
598 
599 	/*
600 	 * Look for the file on the fingerprint lists iff it has not been seen
601 	 * before.
602 	 */
603 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
604 		return (0);
605 
606 	error = mac_veriexec_check_vp(cred, vp, accmode);
607 	return (error);
608 }
609 
610 /**
611  * @brief Unlink on a file has been requested and may need to be validated.
612  *
613  * @param cred		credentials to use
614  * @param dvp		parent directory for file vnode vp
615  * @param dlabel	vnode label assigned to the directory vnode
616  * @param vp		vnode of the file to unlink
617  * @param label		vnode label assigned to the vnode
618  * @param cnp		component name for vp
619  *
620  *
621  * @return 0 if opening the file should be allowed, otherwise an error code.
622  */
623 static int
624 mac_veriexec_vnode_check_unlink(struct ucred *cred, struct vnode *dvp __unused,
625     struct label *dvplabel __unused, struct vnode *vp,
626     struct label *label __unused, struct componentname *cnp __unused)
627 {
628 	int error;
629 
630 	/*
631 	 * Look for the file on the fingerprint lists iff it has not been seen
632 	 * before.
633 	 */
634 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
635 		return (0);
636 
637 	error = mac_veriexec_check_vp(cred, vp, VVERIFY);
638 	if (error == 0) {
639 		/*
640 		 * The target is verified, so disallow replacement.
641 		 */
642 		MAC_VERIEXEC_DBG(2,
643     "(UNLINK) attempted to unlink a protected file (euid: %u)", cred->cr_uid);
644 
645 		return (EAUTH);
646 	}
647 	return (0);
648 }
649 
650 /**
651  * @brief Rename the file has been requested and may need to be validated.
652  *
653  * @param cred		credentials to use
654  * @param dvp		parent directory for file vnode vp
655  * @param dlabel	vnode label assigned to the directory vnode
656  * @param vp		vnode of the file to rename
657  * @param label		vnode label assigned to the vnode
658  * @param cnp		component name for vp
659  *
660  *
661  * @return 0 if opening the file should be allowed, otherwise an error code.
662  */
663 static int
664 mac_veriexec_vnode_check_rename_from(struct ucred *cred,
665     struct vnode *dvp __unused, struct label *dvplabel __unused,
666     struct vnode *vp, struct label *label __unused,
667     struct componentname *cnp __unused)
668 {
669 	int error;
670 
671 	/*
672 	 * Look for the file on the fingerprint lists iff it has not been seen
673 	 * before.
674 	 */
675 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
676 		return (0);
677 
678 	error = mac_veriexec_check_vp(cred, vp, VVERIFY);
679 	if (error == 0) {
680 		/*
681 		 * The target is verified, so disallow replacement.
682 		 */
683 		MAC_VERIEXEC_DBG(2,
684     "(RENAME_FROM) attempted to rename a protected file (euid: %u)", cred->cr_uid);
685 		return (EAUTH);
686 	}
687 	return (0);
688 }
689 
690 
691 /**
692  * @brief Rename to file into the directory (overwrite the file name) has been
693  * requested and may need to be validated.
694  *
695  * @param cred		credentials to use
696  * @param dvp		parent directory for file vnode vp
697  * @param dlabel	vnode label assigned to the directory vnode
698  * @param vp		vnode of the overwritten file
699  * @param label		vnode label assigned to the vnode
700  * @param samedir	1 if the source and destination directories are the same
701  * @param cnp		component name for vp
702  *
703  *
704  * @return 0 if opening the file should be allowed, otherwise an error code.
705  */
706 	static int
707 mac_veriexec_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp __unused,
708     struct label *dvplabel __unused, struct vnode *vp,
709     struct label *label __unused, int samedir __unused,
710     struct componentname *cnp __unused)
711 {
712 	int error;
713 	/*
714 	 * If there is no existing file to overwrite, vp and label will be
715 	 * NULL.
716 	 */
717 	if (vp == NULL)
718 		return (0);
719 
720 	/*
721 	 * Look for the file on the fingerprint lists iff it has not been seen
722 	 * before.
723 	 */
724 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
725 		return (0);
726 
727 	error = mac_veriexec_check_vp(cred, vp, VVERIFY);
728 	if (error == 0) {
729 		/*
730 		 * The target is verified, so disallow replacement.
731 		 */
732 		MAC_VERIEXEC_DBG(2,
733     "(RENAME_TO) attempted to overwrite a protected file (euid: %u)", cred->cr_uid);
734 		return (EAUTH);
735 	}
736 	return (0);
737 }
738 
739 
740 /**
741  * @brief Check mode changes on file to ensure they should be allowed.
742  *
743  * We cannot allow chmod of SUID or SGID on verified files.
744  *
745  * @param cred		credentials to use
746  * @param vp		vnode of the file to open
747  * @param label		vnode label assigned to the vnode
748  * @param mode		mode flags to set
749  *
750  * @return 0 if the mode change should be allowed, EAUTH otherwise.
751  */
752 static int
753 mac_veriexec_vnode_check_setmode(struct ucred *cred, struct vnode *vp,
754     struct label *label __unused, mode_t mode)
755 {
756 	int error;
757 
758 	if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
759 		return (0);
760 
761 	/*
762 	 * Prohibit chmod of verified set-[gu]id file.
763 	 */
764 	error = mac_veriexec_check_vp(cred, vp, VVERIFY);
765 	if (error == EAUTH)		/* target not verified */
766 		return (0);
767 	if (error == 0 && (mode & (S_ISUID|S_ISGID)) != 0)
768 		return (EAUTH);
769 
770 	return (0);
771 }
772 
773 /**
774  * @internal
775  * @brief Initialize the mac_veriexec MAC policy
776  *
777  * @param mpc		MAC policy configuration
778  */
779 static void
780 mac_veriexec_init(struct mac_policy_conf *mpc __unused)
781 {
782 	/* Initialize state */
783 	mac_veriexec_state = VERIEXEC_STATE_INACTIVE;
784 
785 	/* Initialize meta-data storage */
786 	mac_veriexec_metadata_init();
787 
788 	/* Initialize fingerprint ops */
789 	mac_veriexec_fingerprint_init();
790 
791 	/* Register event handlers */
792 	EVENTHANDLER_REGISTER(vfs_mounted, mac_veriexec_vfs_mounted, NULL,
793 	    EVENTHANDLER_PRI_FIRST);
794 	EVENTHANDLER_REGISTER(vfs_unmounted, mac_veriexec_vfs_unmounted, NULL,
795 	    EVENTHANDLER_PRI_LAST);
796 
797 	/* Fetch tunable value in kernel env and define a corresponding read-only sysctl */
798 	mac_veriexec_block_unlink = 0;
799 	TUNABLE_INT_FETCH("security.mac.veriexec.block_unlink", &mac_veriexec_block_unlink);
800 	SYSCTL_INT(_security_mac_veriexec, OID_AUTO, block_unlink,
801 	    CTLFLAG_RDTUN, &mac_veriexec_block_unlink, 0, "Veriexec unlink protection");
802 
803 	/* Check if unlink control is activated via tunable value */
804 	if (!mac_veriexec_block_unlink)
805 		mac_veriexec_ops.mpo_vnode_check_unlink = NULL;
806 }
807 
808 /**
809  * @internal
810  * @brief MAC policy-specific syscall for mac_veriexec
811  *
812  * The following syscalls are implemented:
813  *   - @c MAC_VERIEXEC_CHECK_SYSCALL
814  *        Check if the file referenced by a file descriptor has a fingerprint
815  *        registered in the meta-data store.
816  *
817  * @param td		calling thread
818  * @param call		system call number
819  * @param arg		arugments to the syscall
820  *
821  * @return 0 on success, otherwise an error code.
822  */
823 static int
824 mac_veriexec_syscall(struct thread *td, int call, void *arg)
825 {
826 	struct image_params img;
827 	struct nameidata nd;
828 	cap_rights_t rights;
829 	struct vattr va;
830 	struct file *fp;
831 	struct mac_veriexec_syscall_params_args pargs;
832 	struct mac_veriexec_syscall_params result;
833 	struct mac_veriexec_file_info *ip;
834 	struct proc *proc;
835 	struct vnode *textvp;
836 	int error, flags, proc_locked;
837 
838 	nd.ni_vp = NULL;
839 	proc_locked = 0;
840 	textvp = NULL;
841 	switch (call) {
842 	case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL:
843 	case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL:
844 		error = copyin(arg, &pargs, sizeof(pargs));
845 		if (error)
846 			return error;
847 		break;
848 	}
849 
850 	switch (call) {
851 	case MAC_VERIEXEC_CHECK_FD_SYSCALL:
852 		/* Get the vnode associated with the file descriptor passed */
853 		error = getvnode(td, (uintptr_t) arg,
854 		    cap_rights_init_one(&rights, CAP_READ), &fp);
855 		if (error)
856 			return (error);
857 		if (fp->f_type != DTYPE_VNODE) {
858 			MAC_VERIEXEC_DBG(3, "MAC_VERIEXEC_CHECK_SYSCALL: "
859 			    "file is not vnode type (type=0x%x)",
860 			    fp->f_type);
861 			error = EINVAL;
862 			goto cleanup_file;
863 		}
864 
865 		/*
866 		 * setup the bits of image_params that are used by
867 		 * mac_veriexec_check_fingerprint().
868 		 */
869 		bzero(&img, sizeof(img));
870 		img.proc = td->td_proc;
871 		img.vp = fp->f_vnode;
872 		img.attr = &va;
873 
874 		/*
875 		 * Get vnode attributes
876 		 * (need to obtain a lock on the vnode first)
877 		 */
878 		vn_lock(img.vp, LK_EXCLUSIVE | LK_RETRY);
879 		error = VOP_GETATTR(fp->f_vnode, &va,  td->td_ucred);
880 		if (error)
881 			goto check_done;
882 
883 		MAC_VERIEXEC_DBG(2, "mac_veriexec_fingerprint_check_image: "
884 		    "va_mode=%o, check_files=%d\n", va.va_mode,
885 		    ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0));
886 		error = mac_veriexec_fingerprint_check_image(&img,
887 		    ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0), td);
888 check_done:
889 		/* Release the lock we obtained earlier */
890 		VOP_UNLOCK(img.vp);
891 cleanup_file:
892 		fdrop(fp, td);
893 		break;
894 	case MAC_VERIEXEC_CHECK_PATH_SYSCALL:
895 		/* Look up the path to get the vnode */
896 		NDINIT(&nd, LOOKUP,
897 		    FOLLOW | LOCKLEAF | LOCKSHARED | AUDITVNODE1,
898 		    UIO_USERSPACE, arg);
899 		flags = FREAD;
900 		error = vn_open(&nd, &flags, 0, NULL);
901 		if (error != 0)
902 			break;
903 		NDFREE_PNBUF(&nd);
904 
905 		/* Check the fingerprint status of the vnode */
906 		error = mac_veriexec_check_vp(td->td_ucred, nd.ni_vp, VVERIFY);
907 		/* nd.ni_vp cleaned up below */
908 		break;
909 	case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL:
910 		if (pargs.u.pid == 0 || pargs.u.pid == curproc->p_pid) {
911 			proc = curproc;
912 		} else {
913 			proc = pfind(pargs.u.pid);
914 			if (proc == NULL)
915 				return (EINVAL);
916 			proc_locked = 1;
917 		}
918 		textvp = proc->p_textvp;
919 		/* FALLTHROUGH */
920 	case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL:
921 		if (textvp == NULL) {
922 			/* Look up the path to get the vnode */
923 			NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
924 			    UIO_USERSPACE, pargs.u.filename);
925 			flags = FREAD;
926 			error = vn_open(&nd, &flags, 0, NULL);
927 			if (error != 0)
928 				break;
929 
930 			NDFREE_PNBUF(&nd);
931 			textvp = nd.ni_vp;
932 		}
933 		error = VOP_GETATTR(textvp, &va, curproc->p_ucred);
934 		if (proc_locked)
935 			PROC_UNLOCK(proc);
936 		if (error != 0)
937 			break;
938 
939 		error = mac_veriexec_metadata_get_file_info(va.va_fsid,
940 		    va.va_fileid, va.va_gen, NULL, &ip, FALSE);
941 		if (error != 0)
942 			break;
943 
944 		result.flags = ip->flags;
945 		strlcpy(result.fp_type, ip->ops->type, sizeof(result.fp_type));
946 		result.labellen = ip->labellen;
947 		if (ip->labellen > 0)
948 			strlcpy(result.label, ip->label, sizeof(result.label));
949 		result.label[result.labellen] = '\0';
950 		memcpy(result.fingerprint, ip->fingerprint,
951 		    ip->ops->digest_len);
952 
953 		error = copyout(&result, pargs.params, sizeof(result));
954 		break;
955 	default:
956 		error = EOPNOTSUPP;
957 	}
958 	if (nd.ni_vp != NULL) {
959 		VOP_UNLOCK(nd.ni_vp);
960 		vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
961 	}
962 	return (error);
963 }
964 
965 static struct mac_policy_ops mac_veriexec_ops =
966 {
967 	.mpo_init = mac_veriexec_init,
968 	.mpo_kld_check_load = mac_veriexec_kld_check_load,
969 	.mpo_mount_destroy_label = mac_veriexec_mount_destroy_label,
970 	.mpo_mount_init_label = mac_veriexec_mount_init_label,
971 	.mpo_priv_check = mac_veriexec_priv_check,
972 	.mpo_proc_check_debug = mac_veriexec_proc_check_debug,
973 	.mpo_syscall = mac_veriexec_syscall,
974 	.mpo_system_check_sysctl = mac_veriexec_sysctl_check,
975 	.mpo_vnode_check_exec = mac_veriexec_vnode_check_exec,
976 	.mpo_vnode_check_open = mac_veriexec_vnode_check_open,
977 	.mpo_vnode_check_unlink = mac_veriexec_vnode_check_unlink,
978 	.mpo_vnode_check_rename_to = mac_veriexec_vnode_check_rename_to,
979 	.mpo_vnode_check_rename_from = mac_veriexec_vnode_check_rename_from,
980 	.mpo_vnode_check_setmode = mac_veriexec_vnode_check_setmode,
981 	.mpo_vnode_copy_label = mac_veriexec_copy_label,
982 	.mpo_vnode_destroy_label = mac_veriexec_vnode_destroy_label,
983 	.mpo_vnode_init_label = mac_veriexec_vnode_init_label,
984 };
985 
986 MAC_POLICY_SET(&mac_veriexec_ops, mac_veriexec, MAC_VERIEXEC_FULLNAME,
987     MPC_LOADTIME_FLAG_NOTLATE, &mac_veriexec_slot);
988 MODULE_VERSION(mac_veriexec, MAC_VERIEXEC_VERSION);
989 
990 static struct vnode *
991 mac_veriexec_bottom_vnode(struct vnode *vp)
992 {
993 	struct vnode *ldvp = NULL;
994 
995 	/*
996 	 * XXX This code is bogus. nullfs is not the only stacking
997 	 * filesystem. Less bogus code would add a VOP to reach bottom
998 	 * vnode and would not make assumptions how to get there.
999 	 */
1000 	if (vp->v_mount != NULL &&
1001 	    strcmp(vp->v_mount->mnt_vfc->vfc_name, "nullfs") == 0)
1002 		ldvp = NULLVPTOLOWERVP(vp);
1003 	return (ldvp);
1004 }
1005 
1006 /**
1007  * @brief Get the fingerprint status set on a vnode.
1008  *
1009  * @param vp		vnode to obtain fingerprint status from
1010  *
1011  * @return Fingerprint status assigned to the vnode.
1012  */
1013 fingerprint_status_t
1014 mac_veriexec_get_fingerprint_status(struct vnode *vp)
1015 {
1016 	fingerprint_status_t fps;
1017 	struct vnode *ldvp;
1018 
1019 	fps = SLOT(vp->v_label);
1020 	switch (fps) {
1021 	case FINGERPRINT_VALID:
1022 	case FINGERPRINT_INDIRECT:
1023 	case FINGERPRINT_FILE:
1024 		break;
1025 	default:
1026 		/* we may need to recurse */
1027 		ldvp = mac_veriexec_bottom_vnode(vp);
1028 		if (ldvp != NULL)
1029 			return mac_veriexec_get_fingerprint_status(ldvp);
1030 		break;
1031 	}
1032 	return fps;
1033 }
1034 
1035 /**
1036  * @brief Get the current verified execution subsystem state.
1037  *
1038  * @return Current set of verified execution subsystem state flags.
1039  */
1040 int
1041 mac_veriexec_get_state(void)
1042 {
1043 
1044 	return (mac_veriexec_state);
1045 }
1046 
1047 /**
1048  * @brief Determine if the verified execution subsystem state has specific
1049  *     flags set.
1050  *
1051  * @param state		mask of flags to check
1052  *
1053  * @return State flags set within the masked bits
1054  */
1055 int
1056 mac_veriexec_in_state(int state)
1057 {
1058 
1059 	return (mac_veriexec_state & state);
1060 }
1061 
1062 /**
1063  * @brief Set the fingerprint status for a vnode
1064  *
1065  * Fingerprint status is stored in the MAC per-policy slot assigned to
1066  * mac_veriexec.
1067  *
1068  * @param vp		vnode to store the fingerprint status on
1069  * @param fp_status	fingerprint status to store
1070  */
1071 void
1072 mac_veriexec_set_fingerprint_status(struct vnode *vp,
1073     fingerprint_status_t fp_status)
1074 {
1075 	struct vnode *ldvp;
1076 
1077 	/* recurse until we find the real storage */
1078 	ldvp = mac_veriexec_bottom_vnode(vp);
1079 	if (ldvp != NULL) {
1080 		mac_veriexec_set_fingerprint_status(ldvp, fp_status);
1081 		return;
1082 	}
1083 	SLOT_SET(vp->v_label, fp_status);
1084 }
1085 
1086 /**
1087  * @brief Set verified execution subsystem state flags
1088  *
1089  * @note Flags can only be added to the current state, not removed.
1090  *
1091  * @param state		state flags to add to the current state
1092  */
1093 void
1094 mac_veriexec_set_state(int state)
1095 {
1096 
1097 	mac_veriexec_state |= state;
1098 }
1099 
1100 /**
1101  * @brief Determine if the process is trusted
1102  *
1103  * @param cred		credentials to use
1104  * @param p		the process in question
1105  *
1106  * @return 1 if the process is trusted, otherwise 0.
1107  */
1108 int
1109 mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p)
1110 {
1111 	int already_locked, error, flags;
1112 
1113 	/* Make sure we lock the process if we do not already have the lock */
1114 	already_locked = PROC_LOCKED(p);
1115 	if (!already_locked)
1116 		PROC_LOCK(p);
1117 
1118 	error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
1119 
1120 	/* Unlock the process if we locked it previously */
1121 	if (!already_locked)
1122 		PROC_UNLOCK(p);
1123 
1124 	/* Any errors, deny access */
1125 	if (error != 0)
1126 		return (0);
1127 
1128 	/* Check that the trusted flag is set */
1129 	return ((flags & VERIEXEC_TRUSTED) == VERIEXEC_TRUSTED);
1130 }
1131