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