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