1/* $NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org> 5 * Copyright (c) 2005, 2006 Brett Lymn <blymn@NetBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the authors may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: kern_veriexec.c,v 1.27 2023/04/09 09:18:09 riastradh Exp $"); 33 34#include "opt_veriexec.h" 35 36#include <sys/param.h> 37#include <sys/mount.h> 38#include <sys/kmem.h> 39#include <sys/vnode.h> 40#include <sys/namei.h> 41#include <sys/once.h> 42#include <sys/proc.h> 43#include <sys/rwlock.h> 44#include <sys/syslog.h> 45#include <sys/sysctl.h> 46#include <sys/inttypes.h> 47#include <sys/verified_exec.h> 48#include <sys/sha1.h> 49#include <sys/sha2.h> 50#include <sys/rmd160.h> 51#include <sys/md5.h> 52#include <sys/fileassoc.h> 53#include <sys/kauth.h> 54#include <sys/conf.h> 55#include <miscfs/specfs/specdev.h> 56#include <prop/proplib.h> 57#include <sys/fcntl.h> 58 59/* Readable values for veriexec_file_report(). */ 60#define REPORT_ALWAYS 0x01 /* Always print */ 61#define REPORT_VERBOSE 0x02 /* Print when verbose >= 1 */ 62#define REPORT_DEBUG 0x04 /* Print when verbose >= 2 (debug) */ 63#define REPORT_PANIC 0x08 /* Call panic() */ 64#define REPORT_ALARM 0x10 /* Alarm - also print pid/uid/.. */ 65#define REPORT_LOGMASK (REPORT_ALWAYS|REPORT_VERBOSE|REPORT_DEBUG) 66 67/* state of locking for veriexec_file_verify */ 68#define VERIEXEC_UNLOCKED 0x00 /* Nothing locked, callee does it */ 69#define VERIEXEC_LOCKED 0x01 /* Global op lock held */ 70 71/* state of file locking for veriexec_file_verify */ 72#define VERIEXEC_FILE_UNLOCKED 0x02 /* Nothing locked, callee does it */ 73#define VERIEXEC_FILE_LOCKED 0x04 /* File locked */ 74 75#define VERIEXEC_RW_UPGRADE(lock) while((rw_tryupgrade(lock)) == 0){}; 76 77struct veriexec_fpops { 78 const char *type; 79 size_t hash_len; 80 size_t context_size; 81 veriexec_fpop_init_t init; 82 veriexec_fpop_update_t update; 83 veriexec_fpop_final_t final; 84 LIST_ENTRY(veriexec_fpops) entries; 85}; 86 87/* Veriexec per-file entry data. */ 88struct veriexec_file_entry { 89 krwlock_t lock; /* r/w lock */ 90 u_char *filename; /* File name. */ 91 u_char type; /* Entry type. */ 92 u_char status; /* Evaluation status. */ 93 u_char *fp; /* Fingerprint. */ 94 struct veriexec_fpops *ops; /* Fingerprint ops vector*/ 95 size_t filename_len; /* Length of filename. */ 96}; 97 98/* Veriexec per-table data. */ 99struct veriexec_table_entry { 100 uint64_t vte_count; /* Number of Veriexec entries. */ 101 const struct sysctlnode *vte_node; 102}; 103 104static int veriexec_verbose; 105static int veriexec_strict; 106static int veriexec_bypass = 1; 107 108static char *veriexec_fp_names = NULL; 109static size_t veriexec_name_max = 0; 110 111static const struct sysctlnode *veriexec_count_node; 112 113static fileassoc_t veriexec_hook; 114static specificdata_key_t veriexec_mountspecific_key; 115 116static LIST_HEAD(, veriexec_fpops) veriexec_fpops_list = 117 LIST_HEAD_INITIALIZER(veriexec_fpops_list); 118 119static int veriexec_raw_cb(kauth_cred_t, kauth_action_t, void *, 120 void *, void *, void *, void *); 121static struct veriexec_fpops *veriexec_fpops_lookup(const char *); 122static void veriexec_file_free(struct veriexec_file_entry *); 123 124static unsigned int veriexec_tablecount = 0; 125 126/* 127 * Veriexec operations global lock - most ops hold this as a read 128 * lock, it is upgraded to a write lock when destroying veriexec file 129 * table entries. 130 */ 131static krwlock_t veriexec_op_lock; 132 133/* 134 * Sysctl helper routine for Veriexec. 135 */ 136static int 137sysctl_kern_veriexec_algorithms(SYSCTLFN_ARGS) 138{ 139 size_t len; 140 int error; 141 const char *p; 142 143 if (newp != NULL) 144 return EPERM; 145 146 if (namelen != 0) 147 return EINVAL; 148 149 p = veriexec_fp_names == NULL ? "" : veriexec_fp_names; 150 151 len = strlen(p) + 1; 152 153 if (*oldlenp < len && oldp) 154 return ENOMEM; 155 156 if (oldp && (error = copyout(p, oldp, len)) != 0) 157 return error; 158 159 *oldlenp = len; 160 return 0; 161} 162 163static int 164sysctl_kern_veriexec_strict(SYSCTLFN_ARGS) 165{ 166 struct sysctlnode node; 167 int error, newval; 168 169 node = *rnode; 170 node.sysctl_data = &newval; 171 172 newval = veriexec_strict; 173 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 174 if (error || newp == NULL) 175 return error; 176 177 if (newval < veriexec_strict) 178 return EPERM; 179 180 veriexec_strict = newval; 181 182 return 0; 183} 184 185SYSCTL_SETUP(sysctl_kern_veriexec_setup, "sysctl kern.veriexec setup") 186{ 187 const struct sysctlnode *rnode = NULL; 188 189 sysctl_createv(clog, 0, NULL, &rnode, 190 CTLFLAG_PERMANENT, 191 CTLTYPE_NODE, "veriexec", 192 SYSCTL_DESCR("Veriexec"), 193 NULL, 0, NULL, 0, 194 CTL_KERN, CTL_CREATE, CTL_EOL); 195 196 sysctl_createv(clog, 0, &rnode, NULL, 197 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 198 CTLTYPE_INT, "verbose", 199 SYSCTL_DESCR("Veriexec verbose level"), 200 NULL, 0, &veriexec_verbose, 0, 201 CTL_CREATE, CTL_EOL); 202 sysctl_createv(clog, 0, &rnode, NULL, 203 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 204 CTLTYPE_INT, "strict", 205 SYSCTL_DESCR("Veriexec strict level"), 206 sysctl_kern_veriexec_strict, 0, NULL, 0, 207 CTL_CREATE, CTL_EOL); 208 sysctl_createv(clog, 0, &rnode, NULL, 209 CTLFLAG_PERMANENT, 210 CTLTYPE_STRING, "algorithms", 211 SYSCTL_DESCR("Veriexec supported hashing " 212 "algorithms"), 213 sysctl_kern_veriexec_algorithms, 0, NULL, 0, 214 CTL_CREATE, CTL_EOL); 215 sysctl_createv(clog, 0, &rnode, &veriexec_count_node, 216 CTLFLAG_PERMANENT, 217 CTLTYPE_NODE, "count", 218 SYSCTL_DESCR("Number of fingerprints on mount(s)"), 219 NULL, 0, NULL, 0, 220 CTL_CREATE, CTL_EOL); 221} 222 223/* 224 * Add ops to the fingerprint ops vector list. 225 */ 226int 227veriexec_fpops_add(const char *fp_type, size_t hash_len, size_t ctx_size, 228 veriexec_fpop_init_t init, veriexec_fpop_update_t update, 229 veriexec_fpop_final_t final) 230{ 231 struct veriexec_fpops *ops; 232 233 KASSERT(init != NULL); 234 KASSERT(update != NULL); 235 KASSERT(final != NULL); 236 KASSERT(hash_len != 0); 237 KASSERT(ctx_size != 0); 238 KASSERT(fp_type != NULL); 239 240 if (veriexec_fpops_lookup(fp_type) != NULL) 241 return (EEXIST); 242 243 ops = kmem_alloc(sizeof(*ops), KM_SLEEP); 244 ops->type = fp_type; 245 ops->hash_len = hash_len; 246 ops->context_size = ctx_size; 247 ops->init = init; 248 ops->update = update; 249 ops->final = final; 250 251 LIST_INSERT_HEAD(&veriexec_fpops_list, ops, entries); 252 253 /* 254 * If we don't have space for any names, allocate enough for six 255 * which should be sufficient. (it's also enough for all algorithms 256 * we can support at the moment) 257 */ 258 if (veriexec_fp_names == NULL) { 259 veriexec_name_max = 64; 260 veriexec_fp_names = kmem_zalloc(veriexec_name_max, KM_SLEEP); 261 } 262 263 /* 264 * If we're running out of space for storing supported algorithms, 265 * extend the buffer with space for four names. 266 */ 267 while (veriexec_name_max - (strlen(veriexec_fp_names) + 1) < 268 strlen(fp_type)) { 269 char *newp; 270 unsigned int new_max; 271 272 /* Add space for four algorithm names. */ 273 new_max = veriexec_name_max + 64; 274 newp = kmem_zalloc(new_max, KM_SLEEP); 275 strlcpy(newp, veriexec_fp_names, new_max); 276 kmem_free(veriexec_fp_names, veriexec_name_max); 277 veriexec_fp_names = newp; 278 veriexec_name_max = new_max; 279 } 280 281 if (*veriexec_fp_names != '\0') 282 strlcat(veriexec_fp_names, " ", veriexec_name_max); 283 284 strlcat(veriexec_fp_names, fp_type, veriexec_name_max); 285 286 return (0); 287} 288 289static void 290veriexec_mountspecific_dtor(void *v) 291{ 292 struct veriexec_table_entry *vte = v; 293 294 if (vte == NULL) { 295 return; 296 } 297 sysctl_free(__UNCONST(vte->vte_node)); 298 veriexec_tablecount--; 299 kmem_free(vte, sizeof(*vte)); 300} 301 302static int 303veriexec_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 304 void *arg0, void *arg1, void *arg2, void *arg3) 305{ 306 int result; 307 enum kauth_system_req req; 308 309 if (action != KAUTH_SYSTEM_VERIEXEC) 310 return KAUTH_RESULT_DEFER; 311 312 result = KAUTH_RESULT_DEFER; 313 req = (enum kauth_system_req)(uintptr_t)arg0; 314 315 if (req == KAUTH_REQ_SYSTEM_VERIEXEC_MODIFY && 316 veriexec_strict > VERIEXEC_LEARNING) { 317 log(LOG_WARNING, "Veriexec: Strict mode, modifying " 318 "tables not permitted.\n"); 319 320 result = KAUTH_RESULT_DENY; 321 } 322 323 return result; 324} 325 326/* 327 * Initialise Veriexec. 328 */ 329void 330veriexec_init(void) 331{ 332 int error; 333 334 /* Register a fileassoc for Veriexec. */ 335 error = fileassoc_register("veriexec", 336 (fileassoc_cleanup_cb_t)veriexec_file_free, &veriexec_hook); 337 if (error) 338 panic("Veriexec: Can't register fileassoc: error=%d", error); 339 340 /* Register listener to handle raw disk access. */ 341 if (kauth_listen_scope(KAUTH_SCOPE_DEVICE, veriexec_raw_cb, NULL) == 342 NULL) 343 panic("Veriexec: Can't listen on device scope"); 344 345 error = mount_specific_key_create(&veriexec_mountspecific_key, 346 veriexec_mountspecific_dtor); 347 if (error) 348 panic("Veriexec: Can't create mountspecific key"); 349 350 if (kauth_listen_scope(KAUTH_SCOPE_SYSTEM, veriexec_listener_cb, 351 NULL) == NULL) 352 panic("Veriexec: Can't listen on system scope"); 353 354 rw_init(&veriexec_op_lock); 355 356#define FPOPS_ADD(a, b, c, d, e, f) \ 357 veriexec_fpops_add(a, b, c, \ 358 __FPTRCAST(veriexec_fpop_init_t, d), \ 359 __FPTRCAST(veriexec_fpop_update_t, e), \ 360 __FPTRCAST(veriexec_fpop_final_t, f)) 361 362#ifdef VERIFIED_EXEC_FP_SHA256 363 FPOPS_ADD("SHA256", SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX), 364 SHA256_Init, SHA256_Update, SHA256_Final); 365#endif /* VERIFIED_EXEC_FP_SHA256 */ 366 367#ifdef VERIFIED_EXEC_FP_SHA384 368 FPOPS_ADD("SHA384", SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX), 369 SHA384_Init, SHA384_Update, SHA384_Final); 370#endif /* VERIFIED_EXEC_FP_SHA384 */ 371 372#ifdef VERIFIED_EXEC_FP_SHA512 373 FPOPS_ADD("SHA512", SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX), 374 SHA512_Init, SHA512_Update, SHA512_Final); 375#endif /* VERIFIED_EXEC_FP_SHA512 */ 376 377#undef FPOPS_ADD 378} 379 380static struct veriexec_fpops * 381veriexec_fpops_lookup(const char *name) 382{ 383 struct veriexec_fpops *ops; 384 385 if (name == NULL) 386 return (NULL); 387 388 LIST_FOREACH(ops, &veriexec_fpops_list, entries) { 389 if (strcasecmp(name, ops->type) == 0) 390 return (ops); 391 } 392 393 return (NULL); 394} 395 396/* 397 * Calculate fingerprint. Information on hash length and routines used is 398 * extracted from veriexec_hash_list according to the hash type. 399 * 400 * NOTE: vfe is assumed to be locked for writing on entry. 401 */ 402static int 403veriexec_fp_calc(struct lwp *l, struct vnode *vp, int file_lock_state, 404 struct veriexec_file_entry *vfe, u_char *fp) 405{ 406 struct vattr va; 407 void *ctx; 408 u_char *buf; 409 off_t offset, len; 410 size_t resid; 411 int error; 412 413 KASSERT(file_lock_state != VERIEXEC_LOCKED); 414 KASSERT(file_lock_state != VERIEXEC_UNLOCKED); 415 416 if (file_lock_state == VERIEXEC_FILE_UNLOCKED) 417 vn_lock(vp, LK_SHARED | LK_RETRY); 418 error = VOP_GETATTR(vp, &va, l->l_cred); 419 if (file_lock_state == VERIEXEC_FILE_UNLOCKED) 420 VOP_UNLOCK(vp); 421 if (error) 422 return (error); 423 424 ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP); 425 buf = kmem_alloc(PAGE_SIZE, KM_SLEEP); 426 427 (vfe->ops->init)(ctx); 428 429 len = 0; 430 error = 0; 431 for (offset = 0; offset < va.va_size; offset += PAGE_SIZE) { 432 len = ((va.va_size - offset) < PAGE_SIZE) ? 433 (va.va_size - offset) : PAGE_SIZE; 434 435 error = vn_rdwr(UIO_READ, vp, buf, len, offset, 436 UIO_SYSSPACE, 437 ((file_lock_state == VERIEXEC_FILE_LOCKED)? 438 IO_NODELOCKED : 0), 439 l->l_cred, &resid, NULL); 440 441 if (error) { 442 goto bad; 443 } 444 445 (vfe->ops->update)(ctx, buf, (unsigned int) len); 446 447 if (len != PAGE_SIZE) 448 break; 449 } 450 451 (vfe->ops->final)(fp, ctx); 452 453bad: 454 kmem_free(ctx, vfe->ops->context_size); 455 kmem_free(buf, PAGE_SIZE); 456 457 return (error); 458} 459 460/* Compare two fingerprints of the same type. */ 461static int 462veriexec_fp_cmp(struct veriexec_fpops *ops, u_char *fp1, u_char *fp2) 463{ 464 if (veriexec_verbose >= 2) { 465 int i; 466 467 printf("comparing hashes...\n"); 468 printf("fp1: "); 469 for (i = 0; i < ops->hash_len; i++) { 470 printf("%02x", fp1[i]); 471 } 472 printf("\nfp2: "); 473 for (i = 0; i < ops->hash_len; i++) { 474 printf("%02x", fp2[i]); 475 } 476 printf("\n"); 477 } 478 479 return (memcmp(fp1, fp2, ops->hash_len)); 480} 481 482static int 483veriexec_fp_status(struct lwp *l, struct vnode *vp, int file_lock_state, 484 struct veriexec_file_entry *vfe, u_char *status) 485{ 486 size_t hash_len = vfe->ops->hash_len; 487 u_char *digest; 488 int error; 489 490 digest = kmem_zalloc(hash_len, KM_SLEEP); 491 492 error = veriexec_fp_calc(l, vp, file_lock_state, vfe, digest); 493 if (error) 494 goto out; 495 496 /* Compare fingerprint with loaded data. */ 497 if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0) 498 *status = FINGERPRINT_VALID; 499 else 500 *status = FINGERPRINT_NOMATCH; 501 502out: 503 kmem_free(digest, hash_len); 504 return error; 505} 506 507 508static struct veriexec_table_entry * 509veriexec_table_lookup(struct mount *mp) 510{ 511 /* XXX: From raidframe init */ 512 if (mp == NULL) 513 return NULL; 514 515 return mount_getspecific(mp, veriexec_mountspecific_key); 516} 517 518static struct veriexec_file_entry * 519veriexec_get(struct vnode *vp) 520{ 521 return (fileassoc_lookup(vp, veriexec_hook)); 522} 523 524bool 525veriexec_lookup(struct vnode *vp) 526{ 527 return (veriexec_get(vp) == NULL ? false : true); 528} 529 530/* 531 * Routine for maintaining mostly consistent message formats in Veriexec. 532 */ 533static void 534veriexec_file_report(struct veriexec_file_entry *vfe, const u_char *msg, 535 const u_char *filename, struct lwp *l, int f) 536{ 537 if (vfe != NULL && vfe->filename != NULL) 538 filename = vfe->filename; 539 if (filename == NULL) 540 return; 541 542 if (((f & REPORT_LOGMASK) >> 1) <= veriexec_verbose) { 543 if (!(f & REPORT_ALARM) || (l == NULL)) 544 log(LOG_NOTICE, "Veriexec: %s [%s]\n", msg, 545 filename); 546 else 547 log(LOG_ALERT, "Veriexec: %s [%s, prog=%s pid=%u, " 548 "uid=%u, gid=%u]\n", msg, filename, 549 l->l_proc->p_comm, l->l_proc->p_pid, 550 kauth_cred_getuid(l->l_cred), 551 kauth_cred_getgid(l->l_cred)); 552 } 553 554 if (f & REPORT_PANIC) 555 panic("Veriexec: Unrecoverable error."); 556} 557 558/* 559 * Verify the fingerprint of the given file. If we're called directly from 560 * sys_execve(), 'flag' will be VERIEXEC_DIRECT. If we're called from 561 * exec_script(), 'flag' will be VERIEXEC_INDIRECT. If we are called from 562 * vn_open(), 'flag' will be VERIEXEC_FILE. 563 * 564 * 'veriexec_op_lock' must be locked (and remains locked). 565 * 566 * NOTE: The veriexec file entry pointer (vfep) will be returned LOCKED 567 * on no error. 568 */ 569static int 570veriexec_file_verify(struct lwp *l, struct vnode *vp, const u_char *name, 571 int flag, int file_lock_state, struct veriexec_file_entry **vfep) 572{ 573 struct veriexec_file_entry *vfe; 574 int error = 0; 575 576 KASSERT(rw_lock_held(&veriexec_op_lock)); 577 KASSERT(file_lock_state != VERIEXEC_LOCKED); 578 KASSERT(file_lock_state != VERIEXEC_UNLOCKED); 579 580#define VFE_NEEDS_EVAL(vfe) ((vfe->status == FINGERPRINT_NOTEVAL) || \ 581 (vfe->type & VERIEXEC_UNTRUSTED)) 582 583 if (vfep != NULL) 584 *vfep = NULL; 585 586 if (vp->v_type != VREG) 587 return (0); 588 589 /* Lookup veriexec table entry, save pointer if requested. */ 590 vfe = veriexec_get(vp); 591 if (vfep != NULL) 592 *vfep = vfe; 593 594 /* No entry in the veriexec tables. */ 595 if (vfe == NULL) { 596 veriexec_file_report(NULL, "No entry.", name, 597 l, REPORT_VERBOSE); 598 599 /* 600 * Lockdown mode: Deny access to non-monitored files. 601 * IPS mode: Deny execution of non-monitored files. 602 */ 603 if ((veriexec_strict >= VERIEXEC_LOCKDOWN) || 604 ((veriexec_strict >= VERIEXEC_IPS) && 605 (flag != VERIEXEC_FILE))) 606 return (EPERM); 607 608 return (0); 609 } 610 611 /* 612 * Grab the lock for the entry, if we need to do an evaluation 613 * then the lock is a write lock, after we have the write 614 * lock, check if we really need it - some other thread may 615 * have already done the work for us. 616 */ 617 if (VFE_NEEDS_EVAL(vfe)) { 618 rw_enter(&vfe->lock, RW_WRITER); 619 if (!VFE_NEEDS_EVAL(vfe)) 620 rw_downgrade(&vfe->lock); 621 } else 622 rw_enter(&vfe->lock, RW_READER); 623 624 /* Evaluate fingerprint if needed. */ 625 if (VFE_NEEDS_EVAL(vfe)) { 626 u_char status; 627 628 error = veriexec_fp_status(l, vp, file_lock_state, vfe, &status); 629 if (error) { 630 veriexec_file_report(vfe, "Fingerprint calculation error.", 631 name, NULL, REPORT_ALWAYS); 632 rw_exit(&vfe->lock); 633 return (error); 634 } 635 vfe->status = status; 636 rw_downgrade(&vfe->lock); 637 } 638 639 if (!(vfe->type & flag)) { 640 veriexec_file_report(vfe, "Incorrect access type.", name, l, 641 REPORT_ALWAYS|REPORT_ALARM); 642 643 /* IPS mode: Enforce access type. */ 644 if (veriexec_strict >= VERIEXEC_IPS) { 645 rw_exit(&vfe->lock); 646 return (EPERM); 647 } 648 } 649 650 switch (vfe->status) { 651 case FINGERPRINT_NOTEVAL: 652 /* Should not happen. */ 653 rw_exit(&vfe->lock); 654 veriexec_file_report(vfe, "Not-evaluated status " 655 "post evaluation; inconsistency detected.", name, 656 NULL, REPORT_ALWAYS|REPORT_PANIC); 657 __builtin_unreachable(); 658 /* NOTREACHED */ 659 660 case FINGERPRINT_VALID: 661 /* Valid fingerprint. */ 662 veriexec_file_report(vfe, "Match.", name, NULL, 663 REPORT_VERBOSE); 664 665 break; 666 667 case FINGERPRINT_NOMATCH: 668 /* Fingerprint mismatch. */ 669 veriexec_file_report(vfe, "Mismatch.", name, 670 NULL, REPORT_ALWAYS|REPORT_ALARM); 671 672 /* IDS mode: Deny access on fingerprint mismatch. */ 673 if (veriexec_strict >= VERIEXEC_IDS) { 674 rw_exit(&vfe->lock); 675 error = EPERM; 676 } 677 678 break; 679 680 default: 681 /* Should never happen. */ 682 rw_exit(&vfe->lock); 683 veriexec_file_report(vfe, "Invalid status " 684 "post evaluation.", name, NULL, REPORT_ALWAYS|REPORT_PANIC); 685 /* NOTREACHED */ 686 } 687 688 return (error); 689} 690 691int 692veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag, 693 bool *found) 694{ 695 struct veriexec_file_entry *vfe; 696 int r; 697 698 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING)) 699 return 0; 700 701 rw_enter(&veriexec_op_lock, RW_READER); 702 r = veriexec_file_verify(l, vp, name, flag, VERIEXEC_FILE_UNLOCKED, 703 &vfe); 704 rw_exit(&veriexec_op_lock); 705 706 if ((r == 0) && (vfe != NULL)) 707 rw_exit(&vfe->lock); 708 709 if (found != NULL) 710 *found = (vfe != NULL) ? true : false; 711 712 return (r); 713} 714 715/* 716 * Veriexec remove policy code. 717 */ 718int 719veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf) 720{ 721 struct veriexec_file_entry *vfe; 722 int error; 723 724 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING)) 725 return 0; 726 727 rw_enter(&veriexec_op_lock, RW_READER); 728 vfe = veriexec_get(vp); 729 rw_exit(&veriexec_op_lock); 730 731 if (vfe == NULL) { 732 /* Lockdown mode: Deny access to non-monitored files. */ 733 if (veriexec_strict >= VERIEXEC_LOCKDOWN) 734 return (EPERM); 735 736 return (0); 737 } 738 739 veriexec_file_report(vfe, "Remove request.", pathbuf, l, 740 REPORT_ALWAYS|REPORT_ALARM); 741 742 /* IDS mode: Deny removal of monitored files. */ 743 if (veriexec_strict >= VERIEXEC_IDS) 744 error = EPERM; 745 else 746 error = veriexec_file_delete(l, vp); 747 748 return error; 749} 750 751/* 752 * Veriexec rename policy. 753 * 754 * XXX: Once there's a way to hook after a successful rename, it would be 755 * XXX: nice to update vfe->filename to the new name if it's not NULL and 756 * XXX: the new name is absolute (ie., starts with a slash). 757 */ 758int 759veriexec_renamechk(struct lwp *l, struct vnode *fromvp, const char *fromname, 760 struct vnode *tovp, const char *toname) 761{ 762 struct veriexec_file_entry *fvfe = NULL, *tvfe = NULL; 763 764 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING)) 765 return 0; 766 767 rw_enter(&veriexec_op_lock, RW_READER); 768 769 if (veriexec_strict >= VERIEXEC_LOCKDOWN) { 770 log(LOG_ALERT, "Veriexec: Preventing rename of `%s' to " 771 "`%s', uid=%u, pid=%u: Lockdown mode.\n", fromname, toname, 772 kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid); 773 rw_exit(&veriexec_op_lock); 774 return (EPERM); 775 } 776 777 fvfe = veriexec_get(fromvp); 778 if (tovp != NULL) 779 tvfe = veriexec_get(tovp); 780 781 if ((fvfe == NULL) && (tvfe == NULL)) { 782 /* None of them is monitored */ 783 rw_exit(&veriexec_op_lock); 784 return 0; 785 } 786 787 if (veriexec_strict >= VERIEXEC_IPS) { 788 log(LOG_ALERT, "Veriexec: Preventing rename of `%s' " 789 "to `%s', uid=%u, pid=%u: IPS mode, %s " 790 "monitored.\n", fromname, toname, 791 kauth_cred_geteuid(l->l_cred), 792 l->l_proc->p_pid, (fvfe != NULL && tvfe != NULL) ? 793 "files" : "file"); 794 rw_exit(&veriexec_op_lock); 795 return (EPERM); 796 } 797 798 if (fvfe != NULL) { 799 /* 800 * Monitored file is renamed; filename no longer relevant. 801 */ 802 803 /* 804 * XXX: We could keep the buffer, and when (and if) updating the 805 * XXX: filename post-rename, re-allocate it only if it's not 806 * XXX: big enough for the new filename. 807 */ 808 809 /* XXX: Get write lock on fvfe here? */ 810 811 VERIEXEC_RW_UPGRADE(&veriexec_op_lock); 812 /* once we have the op lock in write mode 813 * there should be no locks on any file 814 * entries so we can destroy the object. 815 */ 816 817 if (fvfe->filename_len > 0) 818 kmem_free(fvfe->filename, fvfe->filename_len); 819 820 fvfe->filename = NULL; 821 fvfe->filename_len = 0; 822 823 rw_downgrade(&veriexec_op_lock); 824 } 825 826 log(LOG_NOTICE, "Veriexec: %s file `%s' renamed to " 827 "%s file `%s', uid=%u, pid=%u.\n", (fvfe != NULL) ? 828 "Monitored" : "Non-monitored", fromname, (tvfe != NULL) ? 829 "monitored" : "non-monitored", toname, 830 kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid); 831 832 rw_exit(&veriexec_op_lock); 833 834 if (tvfe != NULL) { 835 /* 836 * Monitored file is overwritten. Remove the entry. 837 */ 838 (void)veriexec_file_delete(l, tovp); 839 } 840 841 return (0); 842} 843 844static void 845veriexec_file_free(struct veriexec_file_entry *vfe) 846{ 847 if (vfe != NULL) { 848 if (vfe->fp != NULL) 849 kmem_free(vfe->fp, vfe->ops->hash_len); 850 if (vfe->filename != NULL) 851 kmem_free(vfe->filename, vfe->filename_len); 852 rw_destroy(&vfe->lock); 853 kmem_free(vfe, sizeof(*vfe)); 854 } 855} 856 857static void 858veriexec_file_purge(struct veriexec_file_entry *vfe, int have_lock) 859{ 860 if (vfe == NULL) 861 return; 862 863 if (have_lock == VERIEXEC_UNLOCKED) 864 rw_enter(&vfe->lock, RW_WRITER); 865 else 866 VERIEXEC_RW_UPGRADE(&vfe->lock); 867 868 vfe->status = FINGERPRINT_NOTEVAL; 869 if (have_lock == VERIEXEC_UNLOCKED) 870 rw_exit(&vfe->lock); 871 else 872 rw_downgrade(&vfe->lock); 873} 874 875static void 876veriexec_file_purge_cb(struct veriexec_file_entry *vfe, void *cookie) 877{ 878 veriexec_file_purge(vfe, VERIEXEC_UNLOCKED); 879} 880 881/* 882 * Invalidate a Veriexec file entry. 883 * XXX: This should be updated when per-page fingerprints are added. 884 */ 885void 886veriexec_purge(struct vnode *vp) 887{ 888 rw_enter(&veriexec_op_lock, RW_READER); 889 veriexec_file_purge(veriexec_get(vp), VERIEXEC_UNLOCKED); 890 rw_exit(&veriexec_op_lock); 891} 892 893/* 894 * Enforce raw disk access policy. 895 * 896 * IDS mode: Invalidate fingerprints on a mount if it's opened for writing. 897 * IPS mode: Don't allow raw writing to disks we monitor. 898 * Lockdown mode: Don't allow raw writing to all disks. 899 * 900 * XXX: This is bogus. There's an obvious race condition between the time 901 * XXX: the disk is open for writing, in which an attacker can access a 902 * XXX: monitored file to get its signature cached again, and when the raw 903 * XXX: file is overwritten on disk. 904 * XXX: 905 * XXX: To solve this, we need something like the following: 906 * XXX: open raw disk: 907 * XXX: - raise refcount, 908 * XXX: - invalidate fingerprints, 909 * XXX: - mark all entries for that disk with "no cache" flag 910 * XXX: 911 * XXX: veriexec_verify: 912 * XXX: - if "no cache", don't cache evaluation result 913 * XXX: 914 * XXX: close raw disk: 915 * XXX: - lower refcount, 916 * XXX: - if refcount == 0, remove "no cache" flag from all entries 917 */ 918static int 919veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, 920 void *arg0, void *arg1, void *arg2, void *arg3) 921{ 922 int result; 923 enum kauth_device_req req; 924 struct veriexec_table_entry *vte; 925 926 result = KAUTH_RESULT_DENY; 927 req = (enum kauth_device_req)(uintptr_t)arg0; 928 929 switch (action) { 930 case KAUTH_DEVICE_RAWIO_SPEC: { 931 struct vnode *vp, *bvp; 932 int error; 933 934 if (req == KAUTH_REQ_DEVICE_RAWIO_SPEC_READ) { 935 result = KAUTH_RESULT_DEFER; 936 break; 937 } 938 939 vp = arg1; 940 KASSERT(vp != NULL); 941 942 /* Handle /dev/mem and /dev/kmem. */ 943 if (iskmemvp(vp)) { 944 if (veriexec_strict < VERIEXEC_IPS) 945 result = KAUTH_RESULT_DEFER; 946 947 break; 948 } 949 950 error = rawdev_mounted(vp, &bvp); 951 if (error == EINVAL) { 952 result = KAUTH_RESULT_DEFER; 953 break; 954 } 955 956 /* 957 * XXX: See vfs_mountedon() comment in rawdev_mounted(). 958 */ 959 vte = veriexec_table_lookup(bvp->v_mount); 960 if (vte == NULL) { 961 result = KAUTH_RESULT_DEFER; 962 break; 963 } 964 965 switch (veriexec_strict) { 966 case VERIEXEC_LEARNING: 967 case VERIEXEC_IDS: 968 result = KAUTH_RESULT_DEFER; 969 970 rw_enter(&veriexec_op_lock, RW_WRITER); 971 fileassoc_table_run(bvp->v_mount, veriexec_hook, 972 (fileassoc_cb_t)veriexec_file_purge_cb, NULL); 973 rw_exit(&veriexec_op_lock); 974 975 break; 976 case VERIEXEC_IPS: 977 result = KAUTH_RESULT_DENY; 978 break; 979 case VERIEXEC_LOCKDOWN: 980 result = KAUTH_RESULT_DENY; 981 break; 982 } 983 984 break; 985 } 986 987 case KAUTH_DEVICE_RAWIO_PASSTHRU: 988 /* XXX What can we do here? */ 989 if (veriexec_strict < VERIEXEC_IPS) 990 result = KAUTH_RESULT_DEFER; 991 992 break; 993 994 default: 995 result = KAUTH_RESULT_DEFER; 996 break; 997 } 998 999 return (result); 1000} 1001 1002/* 1003 * Create a new Veriexec table. 1004 */ 1005static struct veriexec_table_entry * 1006veriexec_table_add(struct lwp *l, struct mount *mp) 1007{ 1008 struct veriexec_table_entry *vte; 1009 u_char buf[16]; 1010 1011 vte = kmem_zalloc(sizeof(*vte), KM_SLEEP); 1012 mount_setspecific(mp, veriexec_mountspecific_key, vte); 1013 1014 snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++); 1015 sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node, 1016 0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL, 1017 0, CTL_CREATE, CTL_EOL); 1018 1019 sysctl_createv(NULL, 0, &vte->vte_node, NULL, 1020 CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt", 1021 NULL, NULL, 0, mp->mnt_stat.f_mntonname, 1022 0, CTL_CREATE, CTL_EOL); 1023 sysctl_createv(NULL, 0, &vte->vte_node, NULL, 1024 CTLFLAG_READONLY, CTLTYPE_STRING, "fstype", 1025 NULL, NULL, 0, mp->mnt_stat.f_fstypename, 1026 0, CTL_CREATE, CTL_EOL); 1027 sysctl_createv(NULL, 0, &vte->vte_node, NULL, 1028 CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries", 1029 NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL); 1030 1031 return (vte); 1032} 1033 1034/* 1035 * Add a file to be monitored by Veriexec. 1036 * 1037 * Expected elements in dict: 1038 * file, fp, fp-type, entry-type, keep-filename, eval-on-load. 1039 */ 1040int 1041veriexec_file_add(struct lwp *l, prop_dictionary_t dict) 1042{ 1043 struct veriexec_table_entry *vte; 1044 struct veriexec_file_entry *vfe = NULL; 1045 struct veriexec_file_entry *ovfe; 1046 struct vnode *vp; 1047 const char *file, *fp_type; 1048 int error; 1049 bool ignore_dup = false; 1050 1051 if (!prop_dictionary_get_string(dict, "file", &file)) 1052 return (EINVAL); 1053 1054 error = namei_simple_kernel(file, NSM_FOLLOW_NOEMULROOT, &vp); 1055 if (error) 1056 return (error); 1057 1058 /* Add only regular files. */ 1059 if (vp->v_type != VREG) { 1060 log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n", 1061 file); 1062 error = EBADF; 1063 goto out; 1064 } 1065 1066 vfe = kmem_zalloc(sizeof(*vfe), KM_SLEEP); 1067 rw_init(&vfe->lock); 1068 1069 /* Lookup fingerprint hashing algorithm. */ 1070 fp_type = prop_string_value(prop_dictionary_get(dict, "fp-type")); 1071 if ((vfe->ops = veriexec_fpops_lookup(fp_type)) == NULL) { 1072 log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type " 1073 "`%s' for file `%s'.\n", fp_type, file); 1074 error = EOPNOTSUPP; 1075 goto out; 1076 } 1077 1078 if (prop_data_size(prop_dictionary_get(dict, "fp")) != 1079 vfe->ops->hash_len) { 1080 log(LOG_ERR, "Veriexec: Bad fingerprint length for `%s'.\n", 1081 file); 1082 error = EINVAL; 1083 goto out; 1084 } 1085 1086 vfe->fp = kmem_alloc(vfe->ops->hash_len, KM_SLEEP); 1087 memcpy(vfe->fp, prop_data_value(prop_dictionary_get(dict, "fp")), 1088 vfe->ops->hash_len); 1089 1090 rw_enter(&veriexec_op_lock, RW_WRITER); 1091 1092 /* Continue entry initialization. */ 1093 if (prop_dictionary_get_uint8(dict, "entry-type", &vfe->type) == FALSE) 1094 vfe->type = 0; 1095 else { 1096 uint8_t extra_flags; 1097 1098 extra_flags = vfe->type & ~(VERIEXEC_DIRECT | 1099 VERIEXEC_INDIRECT | VERIEXEC_FILE | VERIEXEC_UNTRUSTED); 1100 if (extra_flags) { 1101 log(LOG_NOTICE, "Veriexec: Contaminated flags `0x%x' " 1102 "for `%s', skipping.\n", extra_flags, file); 1103 error = EINVAL; 1104 goto unlock_out; 1105 } 1106 } 1107 if (!(vfe->type & (VERIEXEC_DIRECT | VERIEXEC_INDIRECT | 1108 VERIEXEC_FILE))) 1109 vfe->type |= VERIEXEC_DIRECT; 1110 1111 vfe->status = FINGERPRINT_NOTEVAL; 1112 if (prop_bool_true(prop_dictionary_get(dict, "keep-filename"))) { 1113 vfe->filename = kmem_strdupsize(file, &vfe->filename_len, 1114 KM_SLEEP); 1115 } else 1116 vfe->filename = NULL; 1117 1118 if (prop_bool_true(prop_dictionary_get(dict, "eval-on-load")) || 1119 (vfe->type & VERIEXEC_UNTRUSTED)) { 1120 u_char status; 1121 1122 error = veriexec_fp_status(l, vp, VERIEXEC_FILE_UNLOCKED, 1123 vfe, &status); 1124 if (error) 1125 goto unlock_out; 1126 vfe->status = status; 1127 } 1128 1129 /* 1130 * If we already have an entry for this file, and it matches 1131 * the new entry exactly (except for the filename, which may 1132 * hard-linked!), we just ignore the new entry. If the new 1133 * entry differs, report the error. 1134 */ 1135 if ((ovfe = veriexec_get(vp)) != NULL) { 1136 error = EEXIST; 1137 if (vfe->type == ovfe->type && 1138 vfe->status == ovfe->status && 1139 vfe->ops == ovfe->ops && 1140 memcmp(vfe->fp, ovfe->fp, vfe->ops->hash_len) == 0) 1141 ignore_dup = true; 1142 goto unlock_out; 1143 } 1144 1145 vte = veriexec_table_lookup(vp->v_mount); 1146 if (vte == NULL) 1147 vte = veriexec_table_add(l, vp->v_mount); 1148 1149 /* XXX if we bail below this, we might want to gc newly created vtes. */ 1150 1151 error = fileassoc_add(vp, veriexec_hook, vfe); 1152 if (error) 1153 goto unlock_out; 1154 1155 vte->vte_count++; 1156 1157 veriexec_file_report(NULL, "New entry.", file, NULL, REPORT_DEBUG); 1158 veriexec_bypass = 0; 1159 1160 unlock_out: 1161 rw_exit(&veriexec_op_lock); 1162 1163 out: 1164 vrele(vp); 1165 if (error) 1166 veriexec_file_free(vfe); 1167 1168 if (ignore_dup && error == EEXIST) 1169 error = 0; 1170 1171 return (error); 1172} 1173 1174int 1175veriexec_table_delete(struct lwp *l, struct mount *mp) 1176{ 1177 struct veriexec_table_entry *vte; 1178 1179 vte = veriexec_table_lookup(mp); 1180 if (vte == NULL) 1181 return (ENOENT); 1182 1183 veriexec_mountspecific_dtor(vte); 1184 mount_setspecific(mp, veriexec_mountspecific_key, NULL); 1185 1186 return (fileassoc_table_clear(mp, veriexec_hook)); 1187} 1188 1189int 1190veriexec_file_delete(struct lwp *l, struct vnode *vp) 1191{ 1192 struct veriexec_table_entry *vte; 1193 int error; 1194 1195 vte = veriexec_table_lookup(vp->v_mount); 1196 if (vte == NULL) 1197 return (ENOENT); 1198 1199 rw_enter(&veriexec_op_lock, RW_WRITER); 1200 error = fileassoc_clear(vp, veriexec_hook); 1201 rw_exit(&veriexec_op_lock); 1202 if (!error) { 1203 KASSERT(vte->vte_count > 0); 1204 vte->vte_count--; 1205 } 1206 1207 return (error); 1208} 1209 1210/* 1211 * Convert Veriexec entry data to a dictionary readable by userland tools. 1212 */ 1213static void 1214veriexec_file_convert(struct veriexec_file_entry *vfe, prop_dictionary_t rdict) 1215{ 1216 if (vfe->filename) 1217 prop_dictionary_set(rdict, "file", 1218 prop_string_create_copy(vfe->filename)); 1219 prop_dictionary_set_uint8(rdict, "entry-type", vfe->type); 1220 prop_dictionary_set_uint8(rdict, "status", vfe->status); 1221 prop_dictionary_set(rdict, "fp-type", 1222 prop_string_create_copy(vfe->ops->type)); 1223 prop_dictionary_set(rdict, "fp", 1224 prop_data_create_copy(vfe->fp, vfe->ops->hash_len)); 1225} 1226 1227int 1228veriexec_convert(struct vnode *vp, prop_dictionary_t rdict) 1229{ 1230 struct veriexec_file_entry *vfe; 1231 1232 rw_enter(&veriexec_op_lock, RW_READER); 1233 1234 vfe = veriexec_get(vp); 1235 if (vfe == NULL) { 1236 rw_exit(&veriexec_op_lock); 1237 return (ENOENT); 1238 } 1239 1240 rw_enter(&vfe->lock, RW_READER); 1241 veriexec_file_convert(vfe, rdict); 1242 rw_exit(&vfe->lock); 1243 1244 rw_exit(&veriexec_op_lock); 1245 return (0); 1246} 1247 1248int 1249veriexec_unmountchk(struct mount *mp) 1250{ 1251 int error; 1252 1253 if ((veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING)) 1254 || doing_shutdown) 1255 return (0); 1256 1257 rw_enter(&veriexec_op_lock, RW_READER); 1258 1259 switch (veriexec_strict) { 1260 case VERIEXEC_LEARNING: 1261 error = 0; 1262 break; 1263 1264 case VERIEXEC_IDS: 1265 if (veriexec_table_lookup(mp) != NULL) { 1266 log(LOG_INFO, "Veriexec: IDS mode, allowing unmount " 1267 "of \"%s\".\n", mp->mnt_stat.f_mntonname); 1268 } 1269 1270 error = 0; 1271 break; 1272 1273 case VERIEXEC_IPS: { 1274 struct veriexec_table_entry *vte; 1275 1276 vte = veriexec_table_lookup(mp); 1277 if ((vte != NULL) && (vte->vte_count > 0)) { 1278 log(LOG_ALERT, "Veriexec: IPS mode, preventing" 1279 " unmount of \"%s\" with monitored files.\n", 1280 mp->mnt_stat.f_mntonname); 1281 1282 error = EPERM; 1283 } else 1284 error = 0; 1285 break; 1286 } 1287 1288 case VERIEXEC_LOCKDOWN: 1289 default: 1290 log(LOG_ALERT, "Veriexec: Lockdown mode, preventing unmount " 1291 "of \"%s\".\n", mp->mnt_stat.f_mntonname); 1292 error = EPERM; 1293 break; 1294 } 1295 1296 rw_exit(&veriexec_op_lock); 1297 return (error); 1298} 1299 1300int 1301veriexec_openchk(struct lwp *l, struct vnode *vp, const char *path, int fmode) 1302{ 1303 struct veriexec_file_entry *vfe = NULL; 1304 int error = 0; 1305 1306 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING)) 1307 return 0; 1308 1309 if (vp == NULL) { 1310 /* If no creation requested, let this fail normally. */ 1311 if (!(fmode & O_CREAT)) 1312 goto out; 1313 1314 /* Lockdown mode: Prevent creation of new files. */ 1315 if (veriexec_strict >= VERIEXEC_LOCKDOWN) { 1316 log(LOG_ALERT, "Veriexec: Preventing new file " 1317 "creation in `%s'.\n", path); 1318 error = EPERM; 1319 } 1320 1321 goto out; 1322 } 1323 1324 rw_enter(&veriexec_op_lock, RW_READER); 1325 error = veriexec_file_verify(l, vp, path, VERIEXEC_FILE, 1326 VERIEXEC_FILE_LOCKED, &vfe); 1327 1328 if (error) { 1329 rw_exit(&veriexec_op_lock); 1330 goto out; 1331 } 1332 1333 if ((vfe != NULL) && ((fmode & FWRITE) || (fmode & O_TRUNC))) { 1334 veriexec_file_report(vfe, "Write access request.", path, l, 1335 REPORT_ALWAYS | REPORT_ALARM); 1336 1337 /* IPS mode: Deny write access to monitored files. */ 1338 if (veriexec_strict >= VERIEXEC_IPS) 1339 error = EPERM; 1340 else 1341 veriexec_file_purge(vfe, VERIEXEC_LOCKED); 1342 } 1343 1344 if (vfe != NULL) 1345 rw_exit(&vfe->lock); 1346 1347 rw_exit(&veriexec_op_lock); 1348 out: 1349 return (error); 1350} 1351 1352static void 1353veriexec_file_dump(struct veriexec_file_entry *vfe, prop_array_t entries) 1354{ 1355 prop_dictionary_t entry; 1356 1357 /* If we don't have a filename, this is meaningless. */ 1358 if (vfe->filename == NULL) 1359 return; 1360 1361 entry = prop_dictionary_create(); 1362 1363 veriexec_file_convert(vfe, entry); 1364 1365 prop_array_add(entries, entry); 1366} 1367 1368int 1369veriexec_dump(struct lwp *l, prop_array_t rarray) 1370{ 1371 mount_iterator_t *iter; 1372 struct mount *mp; 1373 1374 mountlist_iterator_init(&iter); 1375 while ((mp = mountlist_iterator_next(iter)) != NULL) { 1376 fileassoc_table_run(mp, veriexec_hook, 1377 (fileassoc_cb_t)veriexec_file_dump, rarray); 1378 } 1379 mountlist_iterator_destroy(iter); 1380 1381 return (0); 1382} 1383 1384int 1385veriexec_flush(struct lwp *l) 1386{ 1387 mount_iterator_t *iter; 1388 struct mount *mp; 1389 int error = 0; 1390 1391 mountlist_iterator_init(&iter); 1392 while ((mp = mountlist_iterator_next(iter)) != NULL) { 1393 int lerror; 1394 1395 lerror = veriexec_table_delete(l, mp); 1396 if (lerror && lerror != ENOENT) 1397 error = lerror; 1398 } 1399 mountlist_iterator_destroy(iter); 1400 1401 return (error); 1402} 1403