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