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