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_VERIEXEC_CONTROL:
439 /*
440 * Do not allow writing to memory or manipulating veriexec,
441 * unless trusted
442 */
443 if (mac_veriexec_proc_is_trusted(cred, curproc) == 0 &&
444 mac_priv_grant(cred, priv) != 0)
445 error = EPERM;
446 MAC_VERIEXEC_DBG(4, "%s priv=%d error=%d", __func__, priv,
447 error);
448 break;
449 default:
450 break;
451 }
452 return (error);
453 }
454
455 /**
456 * @internal
457 * @brief Check if the requested sysctl should be allowed
458 *
459 * @param cred credentials to use
460 * @param oidp sysctl OID
461 * @param arg1 first sysctl argument
462 * @param arg2 second sysctl argument
463 * @param req sysctl request information
464 *
465 * @return 0 if the sysctl should be allowed, otherwise an error code.
466 */
467 static int
mac_veriexec_sysctl_check(struct ucred * cred,struct sysctl_oid * oidp,void * arg1,int arg2,struct sysctl_req * req)468 mac_veriexec_sysctl_check(struct ucred *cred, struct sysctl_oid *oidp,
469 void *arg1, int arg2, struct sysctl_req *req)
470 {
471 struct sysctl_oid *oid;
472
473 /* If we are not enforcing veriexec, nothing for us to check */
474 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
475 return (0);
476
477 oid = oidp;
478 if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) {
479 return (EPERM); /* XXX call mac_veriexec_priv_check? */
480 }
481 return 0;
482 }
483
484 /**
485 * @internal
486 * @brief A program is being executed and needs to be validated.
487 *
488 * @param cred credentials to use
489 * @param vp vnode of the program that is being executed
490 * @param label vnode label assigned to the vnode
491 * @param imgp parameters for the image to be executed
492 * @param execlabel optional exec label
493 *
494 * @return 0 if the program should be allowed to execute, otherwise an error
495 * code.
496 */
497 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)498 mac_veriexec_vnode_check_exec(struct ucred *cred __unused,
499 struct vnode *vp __unused, struct label *label __unused,
500 struct image_params *imgp, struct label *execlabel __unused)
501 {
502 struct thread *td = curthread;
503 int error;
504
505 error = mac_veriexec_fingerprint_check_image(imgp, 0, td);
506 return (error);
507 }
508
509 /**
510 * @brief Check fingerprint for the specified vnode and validate it
511 *
512 * @param cred credentials to use
513 * @param vp vnode of the file
514 * @param accmode access mode to check (read, write, append, create,
515 * verify, etc.)
516 *
517 * @return 0 if the file validated, otherwise an error code.
518 */
519 static int
mac_veriexec_check_vp(struct ucred * cred,struct vnode * vp,accmode_t accmode)520 mac_veriexec_check_vp(struct ucred *cred, struct vnode *vp, accmode_t accmode)
521 {
522 struct vattr va;
523 struct thread *td = curthread;
524 fingerprint_status_t status;
525 int error;
526
527 /* Get vnode attributes */
528 error = VOP_GETATTR(vp, &va, cred);
529 if (error)
530 return (error);
531
532 /* Get the fingerprint status for the file */
533 error = mac_veriexec_metadata_fetch_fingerprint_status(vp, &va, td,
534 VERIEXEC_FILES_FIRST);
535 if (error && error != EAUTH)
536 return (error);
537
538 /*
539 * By now we should have status...
540 */
541 status = mac_veriexec_get_fingerprint_status(vp);
542 if (accmode & VWRITE) {
543 /*
544 * If file has a fingerprint then deny the write request,
545 * otherwise invalidate the status so we don't keep checking
546 * for the file having a fingerprint.
547 */
548 switch (status) {
549 case FINGERPRINT_FILE:
550 case FINGERPRINT_VALID:
551 case FINGERPRINT_INDIRECT:
552 MAC_VERIEXEC_DBG(2,
553 "attempted write to fingerprinted file for dev "
554 "%ju, file %ju.%ju\n", (uintmax_t)va.va_fsid,
555 (uintmax_t)va.va_fileid, (uintmax_t)va.va_gen);
556 return (EPERM);
557 default:
558 break;
559 }
560 }
561 if (accmode & VVERIFY) {
562 switch (status) {
563 case FINGERPRINT_FILE:
564 case FINGERPRINT_VALID:
565 case FINGERPRINT_INDIRECT:
566 if (error)
567 return (error);
568 break;
569 default:
570 /* Allow for overriding verification requirement */
571 if (mac_priv_grant(cred, PRIV_VERIEXEC_NOVERIFY) == 0)
572 return (0);
573 /*
574 * Caller wants open to fail unless there is a valid
575 * fingerprint registered.
576 */
577 MAC_VERIEXEC_DBG(2, "fingerprint status is %d for dev "
578 "%ju, file %ju.%ju\n", status,
579 (uintmax_t)va.va_fsid, (uintmax_t)va.va_fileid,
580 (uintmax_t)va.va_gen);
581 return (EAUTH);
582 }
583 }
584 return (0);
585 }
586
587 /**
588 * @brief Opening a file has been requested and may need to be validated.
589 *
590 * @param cred credentials to use
591 * @param vp vnode of the file to open
592 * @param label vnode label assigned to the vnode
593 * @param accmode access mode to use for opening the file (read, write,
594 * append, create, verify, etc.)
595 *
596 * @return 0 if opening the file should be allowed, otherwise an error code.
597 */
598 static int
mac_veriexec_vnode_check_open(struct ucred * cred,struct vnode * vp,struct label * label __unused,accmode_t accmode)599 mac_veriexec_vnode_check_open(struct ucred *cred, struct vnode *vp,
600 struct label *label __unused, accmode_t accmode)
601 {
602 int error;
603
604 /*
605 * Look for the file on the fingerprint lists iff it has not been seen
606 * before.
607 */
608 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
609 return (0);
610
611 error = mac_veriexec_check_vp(cred, vp, accmode);
612 return (error);
613 }
614
615 /**
616 * @brief Unlink on a file has been requested and may need to be validated.
617 *
618 * @param cred credentials to use
619 * @param dvp parent directory for file vnode vp
620 * @param dlabel vnode label assigned to the directory vnode
621 * @param vp vnode of the file to unlink
622 * @param label vnode label assigned to the vnode
623 * @param cnp component name for vp
624 *
625 *
626 * @return 0 if opening the file should be allowed, otherwise an error code.
627 */
628 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)629 mac_veriexec_vnode_check_unlink(struct ucred *cred, struct vnode *dvp __unused,
630 struct label *dvplabel __unused, struct vnode *vp,
631 struct label *label __unused, struct componentname *cnp __unused)
632 {
633 int error;
634
635 /*
636 * Look for the file on the fingerprint lists iff it has not been seen
637 * before.
638 */
639 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
640 return (0);
641
642 error = mac_veriexec_check_vp(cred, vp, VVERIFY);
643 if (error == 0) {
644 /*
645 * The target is verified, so disallow replacement.
646 */
647 MAC_VERIEXEC_DBG(2,
648 "(UNLINK) attempted to unlink a protected file (euid: %u)", cred->cr_uid);
649
650 return (EAUTH);
651 }
652 return (0);
653 }
654
655 /**
656 * @brief Rename the file has been requested and may need to be validated.
657 *
658 * @param cred credentials to use
659 * @param dvp parent directory for file vnode vp
660 * @param dlabel vnode label assigned to the directory vnode
661 * @param vp vnode of the file to rename
662 * @param label vnode label assigned to the vnode
663 * @param cnp component name for vp
664 *
665 *
666 * @return 0 if opening the file should be allowed, otherwise an error code.
667 */
668 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)669 mac_veriexec_vnode_check_rename_from(struct ucred *cred,
670 struct vnode *dvp __unused, struct label *dvplabel __unused,
671 struct vnode *vp, struct label *label __unused,
672 struct componentname *cnp __unused)
673 {
674 int error;
675
676 /*
677 * Look for the file on the fingerprint lists iff it has not been seen
678 * before.
679 */
680 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
681 return (0);
682
683 error = mac_veriexec_check_vp(cred, vp, VVERIFY);
684 if (error == 0) {
685 /*
686 * The target is verified, so disallow replacement.
687 */
688 MAC_VERIEXEC_DBG(2,
689 "(RENAME_FROM) attempted to rename a protected file (euid: %u)", cred->cr_uid);
690 return (EAUTH);
691 }
692 return (0);
693 }
694
695
696 /**
697 * @brief Rename to file into the directory (overwrite the file name) has been
698 * requested and may need to be validated.
699 *
700 * @param cred credentials to use
701 * @param dvp parent directory for file vnode vp
702 * @param dlabel vnode label assigned to the directory vnode
703 * @param vp vnode of the overwritten file
704 * @param label vnode label assigned to the vnode
705 * @param samedir 1 if the source and destination directories are the same
706 * @param cnp component name for vp
707 *
708 *
709 * @return 0 if opening the file should be allowed, otherwise an error code.
710 */
711 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)712 mac_veriexec_vnode_check_rename_to(struct ucred *cred, struct vnode *dvp __unused,
713 struct label *dvplabel __unused, struct vnode *vp,
714 struct label *label __unused, int samedir __unused,
715 struct componentname *cnp __unused)
716 {
717 int error;
718 /*
719 * If there is no existing file to overwrite, vp and label will be
720 * NULL.
721 */
722 if (vp == NULL)
723 return (0);
724
725 /*
726 * Look for the file on the fingerprint lists iff it has not been seen
727 * before.
728 */
729 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
730 return (0);
731
732 error = mac_veriexec_check_vp(cred, vp, VVERIFY);
733 if (error == 0) {
734 /*
735 * The target is verified, so disallow replacement.
736 */
737 MAC_VERIEXEC_DBG(2,
738 "(RENAME_TO) attempted to overwrite a protected file (euid: %u)", cred->cr_uid);
739 return (EAUTH);
740 }
741 return (0);
742 }
743
744
745 /**
746 * @brief Check mode changes on file to ensure they should be allowed.
747 *
748 * We cannot allow chmod of SUID or SGID on verified files.
749 *
750 * @param cred credentials to use
751 * @param vp vnode of the file to open
752 * @param label vnode label assigned to the vnode
753 * @param mode mode flags to set
754 *
755 * @return 0 if the mode change should be allowed, EAUTH otherwise.
756 */
757 static int
mac_veriexec_vnode_check_setmode(struct ucred * cred,struct vnode * vp,struct label * label __unused,mode_t mode)758 mac_veriexec_vnode_check_setmode(struct ucred *cred, struct vnode *vp,
759 struct label *label __unused, mode_t mode)
760 {
761 int error;
762
763 if ((mac_veriexec_state & VERIEXEC_STATE_ENFORCE) == 0)
764 return (0);
765
766 /*
767 * Prohibit chmod of verified set-[gu]id file.
768 */
769 error = mac_veriexec_check_vp(cred, vp, VVERIFY);
770 if (error == EAUTH) /* target not verified */
771 return (0);
772 if (error == 0 && (mode & (S_ISUID|S_ISGID)) != 0)
773 return (EAUTH);
774
775 return (0);
776 }
777
778 /**
779 * @internal
780 * @brief Initialize the mac_veriexec MAC policy
781 *
782 * @param mpc MAC policy configuration
783 */
784 static void
mac_veriexec_init(struct mac_policy_conf * mpc __unused)785 mac_veriexec_init(struct mac_policy_conf *mpc __unused)
786 {
787 /* Initialize state */
788 mac_veriexec_state = VERIEXEC_STATE_INACTIVE;
789
790 /* Initialize meta-data storage */
791 mac_veriexec_metadata_init();
792
793 /* Initialize fingerprint ops */
794 mac_veriexec_fingerprint_init();
795
796 /* Register event handlers */
797 EVENTHANDLER_REGISTER(vfs_mounted, mac_veriexec_vfs_mounted, NULL,
798 EVENTHANDLER_PRI_FIRST);
799 EVENTHANDLER_REGISTER(vfs_unmounted, mac_veriexec_vfs_unmounted, NULL,
800 EVENTHANDLER_PRI_LAST);
801
802 /* Check if unlink control is activated via tunable value */
803 if (!mac_veriexec_block_unlink)
804 mac_veriexec_ops.mpo_vnode_check_unlink = NULL;
805 }
806
807 #ifdef COMPAT_FREEBSD32
808 struct mac_veriexec_syscall_params32 {
809 char fp_type[VERIEXEC_FPTYPELEN];
810 unsigned char fingerprint[MAXFINGERPRINTLEN];
811 char label[MAXLABELLEN];
812 uint32_t labellen;
813 unsigned char flags;
814 };
815
816 struct mac_veriexec_syscall_params_args32 {
817 union {
818 pid_t pid;
819 uint32_t filename;
820 } u; /* input only */
821 uint32_t params; /* result */
822 };
823 #endif
824
825 /**
826 * @internal
827 * @brief MAC policy-specific syscall for mac_veriexec
828 *
829 * The following syscalls are implemented:
830 * - @c MAC_VERIEXEC_CHECK_SYSCALL
831 * Check if the file referenced by a file descriptor has a fingerprint
832 * registered in the meta-data store.
833 *
834 * @param td calling thread
835 * @param call system call number
836 * @param arg arugments to the syscall
837 *
838 * @return 0 on success, otherwise an error code.
839 */
840 static int
mac_veriexec_syscall(struct thread * td,int call,void * arg)841 mac_veriexec_syscall(struct thread *td, int call, void *arg)
842 {
843 struct image_params img;
844 struct nameidata nd;
845 cap_rights_t rights;
846 struct vattr va;
847 struct file *fp;
848 struct mac_veriexec_syscall_params_args pargs;
849 struct mac_veriexec_syscall_params result;
850 #ifdef COMPAT_FREEBSD32
851 struct mac_veriexec_syscall_params_args32 pargs32;
852 struct mac_veriexec_syscall_params32 result32;
853 #endif
854 struct mac_veriexec_file_info *ip;
855 struct proc *proc;
856 struct vnode *textvp;
857 int error, flags, proc_locked;
858
859 nd.ni_vp = NULL;
860 proc_locked = 0;
861 textvp = NULL;
862 switch (call) {
863 case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL:
864 case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL:
865 #ifdef COMPAT_FREEBSD32
866 if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
867 error = copyin(arg, &pargs32, sizeof(pargs32));
868 if (error)
869 return error;
870 bzero(&pargs, sizeof(pargs));
871 switch (call) {
872 case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL:
873 CP(pargs32, pargs, u.pid);
874 break;
875 case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL:
876 PTRIN_CP(pargs32, pargs, u.filename);
877 break;
878 }
879 PTRIN_CP(pargs32, pargs, params);
880 } else
881 #endif
882 error = copyin(arg, &pargs, sizeof(pargs));
883 if (error)
884 return error;
885 break;
886 }
887
888 switch (call) {
889 case MAC_VERIEXEC_CHECK_FD_SYSCALL:
890 /* Get the vnode associated with the file descriptor passed */
891 error = getvnode(td, (uintptr_t) arg,
892 cap_rights_init_one(&rights, CAP_READ), &fp);
893 if (error)
894 return (error);
895 if (fp->f_type != DTYPE_VNODE) {
896 MAC_VERIEXEC_DBG(3, "MAC_VERIEXEC_CHECK_SYSCALL: "
897 "file is not vnode type (type=0x%x)",
898 fp->f_type);
899 error = EINVAL;
900 goto cleanup_file;
901 }
902
903 /*
904 * setup the bits of image_params that are used by
905 * mac_veriexec_check_fingerprint().
906 */
907 bzero(&img, sizeof(img));
908 img.proc = td->td_proc;
909 img.vp = fp->f_vnode;
910 img.attr = &va;
911
912 /*
913 * Get vnode attributes
914 * (need to obtain a lock on the vnode first)
915 */
916 vn_lock(img.vp, LK_EXCLUSIVE | LK_RETRY);
917 error = VOP_GETATTR(fp->f_vnode, &va, td->td_ucred);
918 if (error)
919 goto check_done;
920
921 MAC_VERIEXEC_DBG(2, "mac_veriexec_fingerprint_check_image: "
922 "va_mode=%o, check_files=%d\n", va.va_mode,
923 ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0));
924 error = mac_veriexec_fingerprint_check_image(&img,
925 ((va.va_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0), td);
926 check_done:
927 /* Release the lock we obtained earlier */
928 VOP_UNLOCK(img.vp);
929 cleanup_file:
930 fdrop(fp, td);
931 break;
932 case MAC_VERIEXEC_CHECK_PATH_SYSCALL:
933 /* Look up the path to get the vnode */
934 NDINIT(&nd, LOOKUP,
935 FOLLOW | LOCKLEAF | LOCKSHARED | AUDITVNODE1,
936 UIO_USERSPACE, arg);
937 flags = FREAD;
938 error = vn_open(&nd, &flags, 0, NULL);
939 if (error != 0)
940 break;
941 NDFREE_PNBUF(&nd);
942
943 /* Check the fingerprint status of the vnode */
944 error = mac_veriexec_check_vp(td->td_ucred, nd.ni_vp, VVERIFY);
945 /* nd.ni_vp cleaned up below */
946 break;
947 case MAC_VERIEXEC_GET_PARAMS_PID_SYSCALL:
948 if (pargs.u.pid == 0 || pargs.u.pid == curproc->p_pid) {
949 proc = curproc;
950 } else {
951 proc = pfind(pargs.u.pid);
952 if (proc == NULL)
953 return (EINVAL);
954 proc_locked = 1;
955 }
956 textvp = proc->p_textvp;
957 /* FALLTHROUGH */
958 case MAC_VERIEXEC_GET_PARAMS_PATH_SYSCALL:
959 if (textvp == NULL) {
960 /* Look up the path to get the vnode */
961 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
962 UIO_USERSPACE, pargs.u.filename);
963 flags = FREAD;
964 error = vn_open(&nd, &flags, 0, NULL);
965 if (error != 0)
966 break;
967
968 NDFREE_PNBUF(&nd);
969 textvp = nd.ni_vp;
970 }
971 error = VOP_GETATTR(textvp, &va, curproc->p_ucred);
972 if (proc_locked)
973 PROC_UNLOCK(proc);
974 if (error != 0)
975 break;
976
977 error = mac_veriexec_metadata_get_file_info(va.va_fsid,
978 va.va_fileid, va.va_gen, NULL, &ip, FALSE);
979 if (error != 0)
980 break;
981
982 #ifdef COMPAT_FREEBSD32
983 if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) {
984 bzero(&result32, sizeof(result32));
985 result32.flags = ip->flags;
986 strlcpy(result32.fp_type, ip->ops->type, sizeof(result32.fp_type));
987 result.labellen = ip->labellen;
988 CP(result, result32, labellen);
989 if (ip->labellen > 0)
990 strlcpy(result32.label, ip->label, sizeof(result32.label));
991 result32.label[result.labellen] = '\0';
992 memcpy(result32.fingerprint, ip->fingerprint,
993 ip->ops->digest_len);
994
995 error = copyout(&result32, pargs.params, sizeof(result32));
996 break; /* yes */
997 }
998 #endif
999 bzero(&result, sizeof(result));
1000 result.flags = ip->flags;
1001 strlcpy(result.fp_type, ip->ops->type, sizeof(result.fp_type));
1002 result.labellen = ip->labellen;
1003 if (ip->labellen > 0)
1004 strlcpy(result.label, ip->label, sizeof(result.label));
1005 result.label[result.labellen] = '\0';
1006 memcpy(result.fingerprint, ip->fingerprint,
1007 ip->ops->digest_len);
1008
1009 error = copyout(&result, pargs.params, sizeof(result));
1010 break;
1011 default:
1012 error = EOPNOTSUPP;
1013 }
1014 if (nd.ni_vp != NULL) {
1015 VOP_UNLOCK(nd.ni_vp);
1016 vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
1017 }
1018 return (error);
1019 }
1020
1021 static struct mac_policy_ops mac_veriexec_ops =
1022 {
1023 .mpo_init = mac_veriexec_init,
1024 .mpo_kld_check_load = mac_veriexec_kld_check_load,
1025 .mpo_mount_destroy_label = mac_veriexec_mount_destroy_label,
1026 .mpo_mount_init_label = mac_veriexec_mount_init_label,
1027 .mpo_priv_check = mac_veriexec_priv_check,
1028 .mpo_proc_check_debug = mac_veriexec_proc_check_debug,
1029 .mpo_syscall = mac_veriexec_syscall,
1030 .mpo_system_check_sysctl = mac_veriexec_sysctl_check,
1031 .mpo_vnode_check_exec = mac_veriexec_vnode_check_exec,
1032 .mpo_vnode_check_open = mac_veriexec_vnode_check_open,
1033 .mpo_vnode_check_unlink = mac_veriexec_vnode_check_unlink,
1034 .mpo_vnode_check_rename_to = mac_veriexec_vnode_check_rename_to,
1035 .mpo_vnode_check_rename_from = mac_veriexec_vnode_check_rename_from,
1036 .mpo_vnode_check_setmode = mac_veriexec_vnode_check_setmode,
1037 .mpo_vnode_copy_label = mac_veriexec_copy_label,
1038 .mpo_vnode_destroy_label = mac_veriexec_vnode_destroy_label,
1039 .mpo_vnode_init_label = mac_veriexec_vnode_init_label,
1040 };
1041
1042 MAC_POLICY_SET(&mac_veriexec_ops, mac_veriexec, MAC_VERIEXEC_FULLNAME,
1043 MPC_LOADTIME_FLAG_NOTLATE, &mac_veriexec_slot);
1044 MODULE_VERSION(mac_veriexec, MAC_VERIEXEC_VERSION);
1045
1046 static struct vnode *
mac_veriexec_bottom_vnode(struct vnode * vp)1047 mac_veriexec_bottom_vnode(struct vnode *vp)
1048 {
1049 struct vnode *ldvp = NULL;
1050
1051 /*
1052 * XXX This code is bogus. nullfs is not the only stacking
1053 * filesystem. Less bogus code would add a VOP to reach bottom
1054 * vnode and would not make assumptions how to get there.
1055 */
1056 if (vp->v_mount != NULL &&
1057 strcmp(vp->v_mount->mnt_vfc->vfc_name, "nullfs") == 0)
1058 ldvp = NULLVPTOLOWERVP(vp);
1059 return (ldvp);
1060 }
1061
1062 /**
1063 * @brief Get the fingerprint status set on a vnode.
1064 *
1065 * @param vp vnode to obtain fingerprint status from
1066 *
1067 * @return Fingerprint status assigned to the vnode.
1068 */
1069 fingerprint_status_t
mac_veriexec_get_fingerprint_status(struct vnode * vp)1070 mac_veriexec_get_fingerprint_status(struct vnode *vp)
1071 {
1072 fingerprint_status_t fps;
1073 struct vnode *ldvp;
1074
1075 fps = SLOT(vp->v_label);
1076 switch (fps) {
1077 case FINGERPRINT_VALID:
1078 case FINGERPRINT_INDIRECT:
1079 case FINGERPRINT_FILE:
1080 break;
1081 default:
1082 /* we may need to recurse */
1083 ldvp = mac_veriexec_bottom_vnode(vp);
1084 if (ldvp != NULL)
1085 return mac_veriexec_get_fingerprint_status(ldvp);
1086 break;
1087 }
1088 return fps;
1089 }
1090
1091 /**
1092 * @brief Get the current verified execution subsystem state.
1093 *
1094 * @return Current set of verified execution subsystem state flags.
1095 */
1096 int
mac_veriexec_get_state(void)1097 mac_veriexec_get_state(void)
1098 {
1099
1100 return (mac_veriexec_state);
1101 }
1102
1103 /**
1104 * @brief Determine if the verified execution subsystem state has specific
1105 * flags set.
1106 *
1107 * @param state mask of flags to check
1108 *
1109 * @return State flags set within the masked bits
1110 */
1111 int
mac_veriexec_in_state(int state)1112 mac_veriexec_in_state(int state)
1113 {
1114
1115 return (mac_veriexec_state & state);
1116 }
1117
1118 /**
1119 * @brief Set the fingerprint status for a vnode
1120 *
1121 * Fingerprint status is stored in the MAC per-policy slot assigned to
1122 * mac_veriexec.
1123 *
1124 * @param vp vnode to store the fingerprint status on
1125 * @param fp_status fingerprint status to store
1126 */
1127 void
mac_veriexec_set_fingerprint_status(struct vnode * vp,fingerprint_status_t fp_status)1128 mac_veriexec_set_fingerprint_status(struct vnode *vp,
1129 fingerprint_status_t fp_status)
1130 {
1131 struct vnode *ldvp;
1132
1133 /* recurse until we find the real storage */
1134 ldvp = mac_veriexec_bottom_vnode(vp);
1135 if (ldvp != NULL) {
1136 mac_veriexec_set_fingerprint_status(ldvp, fp_status);
1137 return;
1138 }
1139 SLOT_SET(vp->v_label, fp_status);
1140 }
1141
1142 /**
1143 * @brief Set verified execution subsystem state flags
1144 *
1145 * @note Flags can only be added to the current state, not removed.
1146 *
1147 * @param state state flags to add to the current state
1148 */
1149 void
mac_veriexec_set_state(int state)1150 mac_veriexec_set_state(int state)
1151 {
1152
1153 mac_veriexec_state |= state;
1154 }
1155
1156 /**
1157 * @brief Determine if the process is trusted
1158 *
1159 * @param cred credentials to use
1160 * @param p the process in question
1161 *
1162 * @return 1 if the process is trusted, otherwise 0.
1163 */
1164 int
mac_veriexec_proc_is_trusted(struct ucred * cred,struct proc * p)1165 mac_veriexec_proc_is_trusted(struct ucred *cred, struct proc *p)
1166 {
1167 int already_locked, error, flags;
1168
1169 /* Make sure we lock the process if we do not already have the lock */
1170 already_locked = PROC_LOCKED(p);
1171 if (!already_locked)
1172 PROC_LOCK(p);
1173
1174 error = mac_veriexec_metadata_get_executable_flags(cred, p, &flags, 0);
1175
1176 /* Unlock the process if we locked it previously */
1177 if (!already_locked)
1178 PROC_UNLOCK(p);
1179
1180 /* Any errors, deny access */
1181 if (error != 0)
1182 return (0);
1183
1184 /* Check that the trusted flag is set */
1185 return ((flags & VERIEXEC_TRUSTED) == VERIEXEC_TRUSTED);
1186 }
1187