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