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