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