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