Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / kern / kern_verifiedexec.c
blobea130f07c22a49e98259500401a2546c953701de
1 /* $NetBSD: kern_verifiedexec.c,v 1.120 2009/12/28 02:35:20 elad Exp $ */
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.
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.
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.
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: kern_verifiedexec.c,v 1.120 2009/12/28 02:35:20 elad Exp $");
34 #include "opt_veriexec.h"
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/exec.h>
42 #include <sys/once.h>
43 #include <sys/proc.h>
44 #include <sys/rwlock.h>
45 #include <sys/syslog.h>
46 #include <sys/sysctl.h>
47 #include <sys/inttypes.h>
48 #include <sys/verified_exec.h>
49 #if defined(__FreeBSD__)
50 # include <sys/systm.h>
51 # include <sys/imgact.h>
52 # include <crypto/sha1.h>
53 # include <crypto/sha2/sha2.h>
54 # include <crypto/ripemd160/rmd160.h>
55 #else
56 # include <sys/sha1.h>
57 # include <sys/sha2.h>
58 # include <sys/rmd160.h>
59 #endif
60 #include <sys/md5.h>
61 #include <uvm/uvm_extern.h>
62 #include <sys/fileassoc.h>
63 #include <sys/kauth.h>
64 #include <sys/conf.h>
65 #include <miscfs/specfs/specdev.h>
66 #include <prop/proplib.h>
67 #include <sys/fcntl.h>
69 /* Readable values for veriexec_file_report(). */
70 #define REPORT_ALWAYS 0x01 /* Always print */
71 #define REPORT_VERBOSE 0x02 /* Print when verbose >= 1 */
72 #define REPORT_DEBUG 0x04 /* Print when verbose >= 2 (debug) */
73 #define REPORT_PANIC 0x08 /* Call panic() */
74 #define REPORT_ALARM 0x10 /* Alarm - also print pid/uid/.. */
75 #define REPORT_LOGMASK (REPORT_ALWAYS|REPORT_VERBOSE|REPORT_DEBUG)
77 /* state of locking for veriexec_file_verify */
78 #define VERIEXEC_UNLOCKED 0x00 /* Nothing locked, callee does it */
79 #define VERIEXEC_LOCKED 0x01 /* Global op lock held */
82 #define VERIEXEC_RW_UPGRADE(lock) while((rw_tryupgrade(lock)) == 0){};
84 struct veriexec_fpops {
85 const char *type;
86 size_t hash_len;
87 size_t context_size;
88 veriexec_fpop_init_t init;
89 veriexec_fpop_update_t update;
90 veriexec_fpop_final_t final;
91 LIST_ENTRY(veriexec_fpops) entries;
94 /* Veriexec per-file entry data. */
95 struct veriexec_file_entry {
96 krwlock_t lock; /* r/w lock */
97 u_char *filename; /* File name. */
98 u_char type; /* Entry type. */
99 u_char status; /* Evaluation status. */
100 u_char page_fp_status; /* Per-page FP status. */
101 u_char *fp; /* Fingerprint. */
102 void *page_fp; /* Per-page fingerprints */
103 size_t npages; /* Number of pages. */
104 size_t last_page_size; /* To support < PAGE_SIZE */
105 struct veriexec_fpops *ops; /* Fingerprint ops vector*/
106 size_t filename_len; /* Length of filename. */
109 /* Veriexec per-table data. */
110 struct veriexec_table_entry {
111 uint64_t vte_count; /* Number of Veriexec entries. */
112 const struct sysctlnode *vte_node;
115 static int veriexec_verbose;
116 int veriexec_strict;
117 static int veriexec_bypass = 1;
119 static char *veriexec_fp_names = NULL;
120 static size_t veriexec_name_max = 0;
122 static const struct sysctlnode *veriexec_count_node;
124 static fileassoc_t veriexec_hook;
125 static specificdata_key_t veriexec_mountspecific_key;
127 static LIST_HEAD(, veriexec_fpops) veriexec_fpops_list =
128 LIST_HEAD_INITIALIZER(veriexec_fpops_list);
130 static int veriexec_raw_cb(kauth_cred_t, kauth_action_t, void *,
131 void *, void *, void *, void *);
132 static struct veriexec_fpops *veriexec_fpops_lookup(const char *);
133 static void veriexec_file_free(struct veriexec_file_entry *);
135 static unsigned int veriexec_tablecount = 0;
138 * Veriexec operations global lock - most ops hold this as a read
139 * lock, it is upgraded to a write lock when destroying veriexec file
140 * table entries.
142 static krwlock_t veriexec_op_lock;
145 * Sysctl helper routine for Veriexec.
147 static int
148 sysctl_kern_veriexec(SYSCTLFN_ARGS)
150 int newval, error;
151 int *var = NULL, raise_only = 0;
152 struct sysctlnode node;
154 node = *rnode;
156 if (strcmp(rnode->sysctl_name, "strict") == 0) {
157 raise_only = 1;
158 var = &veriexec_strict;
159 } else if (strcmp(rnode->sysctl_name, "algorithms") == 0) {
160 node.sysctl_data = veriexec_fp_names;
161 node.sysctl_size = strlen(veriexec_fp_names) + 1;
162 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
163 } else {
164 return (EINVAL);
167 newval = *var;
169 node.sysctl_data = &newval;
170 error = sysctl_lookup(SYSCTLFN_CALL(&node));
171 if (error || newp == NULL) {
172 return (error);
175 if (raise_only && (newval < *var))
176 return (EPERM);
178 *var = newval;
180 return (error);
183 SYSCTL_SETUP(sysctl_kern_veriexec_setup, "sysctl kern.veriexec setup")
185 const struct sysctlnode *rnode = NULL;
187 sysctl_createv(clog, 0, NULL, &rnode,
188 CTLFLAG_PERMANENT,
189 CTLTYPE_NODE, "kern", NULL,
190 NULL, 0, NULL, 0,
191 CTL_KERN, CTL_EOL);
193 sysctl_createv(clog, 0, &rnode, &rnode,
194 CTLFLAG_PERMANENT,
195 CTLTYPE_NODE, "veriexec",
196 SYSCTL_DESCR("Veriexec"),
197 NULL, 0, NULL, 0,
198 CTL_CREATE, CTL_EOL);
200 sysctl_createv(clog, 0, &rnode, NULL,
201 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
202 CTLTYPE_INT, "verbose",
203 SYSCTL_DESCR("Veriexec verbose level"),
204 NULL, 0, &veriexec_verbose, 0,
205 CTL_CREATE, CTL_EOL);
206 sysctl_createv(clog, 0, &rnode, NULL,
207 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
208 CTLTYPE_INT, "strict",
209 SYSCTL_DESCR("Veriexec strict level"),
210 sysctl_kern_veriexec, 0, NULL, 0,
211 CTL_CREATE, CTL_EOL);
212 sysctl_createv(clog, 0, &rnode, NULL,
213 CTLFLAG_PERMANENT,
214 CTLTYPE_STRING, "algorithms",
215 SYSCTL_DESCR("Veriexec supported hashing "
216 "algorithms"),
217 sysctl_kern_veriexec, 0, NULL, 0,
218 CTL_CREATE, CTL_EOL);
219 sysctl_createv(clog, 0, &rnode, &veriexec_count_node,
220 CTLFLAG_PERMANENT,
221 CTLTYPE_NODE, "count",
222 SYSCTL_DESCR("Number of fingerprints on mount(s)"),
223 NULL, 0, NULL, 0,
224 CTL_CREATE, CTL_EOL);
228 * Add ops to the fignerprint ops vector list.
231 veriexec_fpops_add(const char *fp_type, size_t hash_len, size_t ctx_size,
232 veriexec_fpop_init_t init, veriexec_fpop_update_t update,
233 veriexec_fpop_final_t final)
235 struct veriexec_fpops *ops;
237 /* Sanity check all parameters. */
238 if ((fp_type == NULL) || (hash_len == 0) || (ctx_size == 0) ||
239 (init == NULL) || (update == NULL) || (final == NULL))
240 return (EFAULT);
242 if (veriexec_fpops_lookup(fp_type) != NULL)
243 return (EEXIST);
245 ops = kmem_alloc(sizeof(*ops), KM_SLEEP);
247 ops->type = fp_type;
248 ops->hash_len = hash_len;
249 ops->context_size = ctx_size;
250 ops->init = init;
251 ops->update = update;
252 ops->final = final;
254 LIST_INSERT_HEAD(&veriexec_fpops_list, ops, entries);
257 * If we don't have space for any names, allocate enough for six
258 * which should be sufficient. (it's also enough for all algorithms
259 * we can support at the moment)
261 if (veriexec_fp_names == NULL) {
262 veriexec_name_max = 64;
263 veriexec_fp_names = kmem_zalloc(veriexec_name_max, KM_SLEEP);
267 * If we're running out of space for storing supported algorithms,
268 * extend the buffer with space for four names.
270 while (veriexec_name_max - (strlen(veriexec_fp_names) + 1) <
271 strlen(fp_type)) {
272 char *newp;
273 unsigned int new_max;
275 /* Add space for four algorithm names. */
276 new_max = veriexec_name_max + 64;
277 newp = kmem_zalloc(new_max, KM_SLEEP);
278 strlcpy(newp, veriexec_fp_names, new_max);
279 kmem_free(veriexec_fp_names, veriexec_name_max);
280 veriexec_fp_names = newp;
281 veriexec_name_max = new_max;
284 if (*veriexec_fp_names != '\0')
285 strlcat(veriexec_fp_names, " ", veriexec_name_max);
287 strlcat(veriexec_fp_names, fp_type, veriexec_name_max);
289 return (0);
292 static void
293 veriexec_mountspecific_dtor(void *v)
295 struct veriexec_table_entry *vte = v;
297 if (vte == NULL) {
298 return;
300 sysctl_free(__UNCONST(vte->vte_node));
301 veriexec_tablecount--;
302 kmem_free(vte, sizeof(*vte));
306 * Initialise Veriexec.
308 void
309 veriexec_init(void)
311 int error;
313 /* Register a fileassoc for Veriexec. */
314 error = fileassoc_register("veriexec",
315 (fileassoc_cleanup_cb_t)veriexec_file_free, &veriexec_hook);
316 if (error)
317 panic("Veriexec: Can't register fileassoc: error=%d", error);
319 /* Register listener to handle raw disk access. */
320 if (kauth_listen_scope(KAUTH_SCOPE_DEVICE, veriexec_raw_cb, NULL) ==
321 NULL)
322 panic("Veriexec: Can't listen on device scope");
324 error = mount_specific_key_create(&veriexec_mountspecific_key,
325 veriexec_mountspecific_dtor);
326 if (error)
327 panic("Veriexec: Can't create mountspecific key");
329 rw_init(&veriexec_op_lock);
331 #define FPOPS_ADD(a, b, c, d, e, f) \
332 veriexec_fpops_add(a, b, c, (veriexec_fpop_init_t)d, \
333 (veriexec_fpop_update_t)e, (veriexec_fpop_final_t)f)
335 #ifdef VERIFIED_EXEC_FP_RMD160
336 FPOPS_ADD("RMD160", RMD160_DIGEST_LENGTH, sizeof(RMD160_CTX),
337 RMD160Init, RMD160Update, RMD160Final);
338 #endif /* VERIFIED_EXEC_FP_RMD160 */
340 #ifdef VERIFIED_EXEC_FP_SHA256
341 FPOPS_ADD("SHA256", SHA256_DIGEST_LENGTH, sizeof(SHA256_CTX),
342 SHA256_Init, SHA256_Update, SHA256_Final);
343 #endif /* VERIFIED_EXEC_FP_SHA256 */
345 #ifdef VERIFIED_EXEC_FP_SHA384
346 FPOPS_ADD("SHA384", SHA384_DIGEST_LENGTH, sizeof(SHA384_CTX),
347 SHA384_Init, SHA384_Update, SHA384_Final);
348 #endif /* VERIFIED_EXEC_FP_SHA384 */
350 #ifdef VERIFIED_EXEC_FP_SHA512
351 FPOPS_ADD("SHA512", SHA512_DIGEST_LENGTH, sizeof(SHA512_CTX),
352 SHA512_Init, SHA512_Update, SHA512_Final);
353 #endif /* VERIFIED_EXEC_FP_SHA512 */
355 #ifdef VERIFIED_EXEC_FP_SHA1
356 FPOPS_ADD("SHA1", SHA1_DIGEST_LENGTH, sizeof(SHA1_CTX),
357 SHA1Init, SHA1Update, SHA1Final);
358 #endif /* VERIFIED_EXEC_FP_SHA1 */
360 #ifdef VERIFIED_EXEC_FP_MD5
361 FPOPS_ADD("MD5", MD5_DIGEST_LENGTH, sizeof(MD5_CTX),
362 MD5Init, MD5Update, MD5Final);
363 #endif /* VERIFIED_EXEC_FP_MD5 */
365 #undef FPOPS_ADD
368 static struct veriexec_fpops *
369 veriexec_fpops_lookup(const char *name)
371 struct veriexec_fpops *ops;
373 if (name == NULL)
374 return (NULL);
376 LIST_FOREACH(ops, &veriexec_fpops_list, entries) {
377 if (strcasecmp(name, ops->type) == 0)
378 return (ops);
381 return (NULL);
385 * Calculate fingerprint. Information on hash length and routines used is
386 * extracted from veriexec_hash_list according to the hash type.
388 * NOTE: vfe is assumed to be locked for writing on entry.
390 static int
391 veriexec_fp_calc(struct lwp *l, struct vnode *vp, int lock_state,
392 struct veriexec_file_entry *vfe, u_char *fp)
394 struct vattr va;
395 void *ctx, *page_ctx;
396 u_char *buf, *page_fp;
397 off_t offset, len;
398 size_t resid, npages;
399 int error, do_perpage, pagen;
401 error = VOP_GETATTR(vp, &va, l->l_cred);
402 if (error)
403 return (error);
405 #ifdef notyet /* XXX - for now */
406 if ((vfe->type & VERIEXEC_UNTRUSTED) &&
407 (vfe->page_fp_status == PAGE_FP_NONE))
408 do_perpage = 1;
409 else
410 #endif /* notyet */
411 do_perpage = 0;
413 ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP);
414 buf = kmem_alloc(PAGE_SIZE, KM_SLEEP);
416 page_ctx = NULL;
417 page_fp = NULL;
418 npages = 0;
419 if (do_perpage) {
420 npages = (va.va_size >> PAGE_SHIFT) + 1;
421 page_fp = kmem_alloc(vfe->ops->hash_len * npages, KM_SLEEP);
422 vfe->page_fp = page_fp;
423 page_ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP);
426 (vfe->ops->init)(ctx);
428 len = 0;
429 error = 0;
430 pagen = 0;
431 for (offset = 0; offset < va.va_size; offset += PAGE_SIZE) {
432 len = ((va.va_size - offset) < PAGE_SIZE) ?
433 (va.va_size - offset) : PAGE_SIZE;
435 error = vn_rdwr(UIO_READ, vp, buf, len, offset,
436 UIO_SYSSPACE,
437 ((lock_state == VERIEXEC_LOCKED)?
438 IO_NODELOCKED : 0),
439 l->l_cred, &resid, NULL);
441 if (error) {
442 if (do_perpage) {
443 kmem_free(vfe->page_fp,
444 vfe->ops->hash_len * npages);
445 vfe->page_fp = NULL;
448 goto bad;
451 (vfe->ops->update)(ctx, buf, (unsigned int) len);
453 if (do_perpage) {
454 (vfe->ops->init)(page_ctx);
455 (vfe->ops->update)(page_ctx, buf, (unsigned int)len);
456 (vfe->ops->final)(page_fp, page_ctx);
458 if (veriexec_verbose >= 2) {
459 int i;
461 printf("hash for page %d: ", pagen);
462 for (i = 0; i < vfe->ops->hash_len; i++)
463 printf("%02x", page_fp[i]);
464 printf("\n");
467 page_fp += vfe->ops->hash_len;
468 pagen++;
471 if (len != PAGE_SIZE)
472 break;
475 (vfe->ops->final)(fp, ctx);
477 if (do_perpage) {
478 vfe->last_page_size = len;
479 vfe->page_fp_status = PAGE_FP_READY;
480 vfe->npages = npages;
483 bad:
484 if (do_perpage)
485 kmem_free(page_ctx, vfe->ops->context_size);
487 kmem_free(ctx, vfe->ops->context_size);
488 kmem_free(buf, PAGE_SIZE);
490 return (error);
493 /* Compare two fingerprints of the same type. */
494 static int
495 veriexec_fp_cmp(struct veriexec_fpops *ops, u_char *fp1, u_char *fp2)
497 if (veriexec_verbose >= 2) {
498 int i;
500 printf("comparing hashes...\n");
501 printf("fp1: ");
502 for (i = 0; i < ops->hash_len; i++) {
503 printf("%02x", fp1[i]);
505 printf("\nfp2: ");
506 for (i = 0; i < ops->hash_len; i++) {
507 printf("%02x", fp2[i]);
509 printf("\n");
512 return (memcmp(fp1, fp2, ops->hash_len));
515 static struct veriexec_table_entry *
516 veriexec_table_lookup(struct mount *mp)
518 /* XXX: From raidframe init */
519 if (mp == NULL)
520 return NULL;
522 return mount_getspecific(mp, veriexec_mountspecific_key);
525 static struct veriexec_file_entry *
526 veriexec_get(struct vnode *vp)
528 return (fileassoc_lookup(vp, veriexec_hook));
531 bool
532 veriexec_lookup(struct vnode *vp)
534 return (veriexec_get(vp) == NULL ? false : true);
538 * Routine for maintaining mostly consistent message formats in Veriexec.
540 static void
541 veriexec_file_report(struct veriexec_file_entry *vfe, const u_char *msg,
542 const u_char *filename, struct lwp *l, int f)
544 if (msg == NULL)
545 return;
547 if (vfe != NULL && vfe->filename != NULL)
548 filename = vfe->filename;
550 if (filename == NULL)
551 return;
553 if (((f & REPORT_LOGMASK) >> 1) <= veriexec_verbose) {
554 if (!(f & REPORT_ALARM) || (l == NULL))
555 log(LOG_NOTICE, "Veriexec: %s [%s]\n", msg,
556 filename);
557 else
558 log(LOG_ALERT, "Veriexec: %s [%s, prog=%s pid=%u, "
559 "uid=%u, gid=%u]\n", msg, filename,
560 l->l_proc->p_comm, l->l_proc->p_pid,
561 kauth_cred_getuid(l->l_cred),
562 kauth_cred_getgid(l->l_cred));
565 if (f & REPORT_PANIC)
566 panic("Veriexec: Unrecoverable error.");
570 * Verify the fingerprint of the given file. If we're called directly from
571 * sys_execve(), 'flag' will be VERIEXEC_DIRECT. If we're called from
572 * exec_script(), 'flag' will be VERIEXEC_INDIRECT. If we are called from
573 * vn_open(), 'flag' will be VERIEXEC_FILE.
575 * NOTE: The veriexec file entry pointer (vfep) will be returned LOCKED
576 * on no error.
578 static int
579 veriexec_file_verify(struct lwp *l, struct vnode *vp, const u_char *name,
580 int flag, int lockstate, struct veriexec_file_entry **vfep)
582 struct veriexec_file_entry *vfe;
583 int error;
585 #define VFE_NEEDS_EVAL(vfe) ((vfe->status == FINGERPRINT_NOTEVAL) || \
586 (vfe->type & VERIEXEC_UNTRUSTED))
588 if (vfep != NULL)
589 *vfep = NULL;
591 if (vp->v_type != VREG)
592 return (0);
594 if (lockstate == VERIEXEC_UNLOCKED)
595 rw_enter(&veriexec_op_lock, RW_READER);
597 /* Lookup veriexec table entry, save pointer if requested. */
598 vfe = veriexec_get(vp);
599 if (vfep != NULL)
600 *vfep = vfe;
601 if (vfe == NULL)
602 goto out;
604 error = 0;
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.
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);
619 /* Evaluate fingerprint if needed. */
620 if (VFE_NEEDS_EVAL(vfe)) {
621 u_char *digest;
623 /* Calculate fingerprint for on-disk file. */
624 digest = kmem_zalloc(vfe->ops->hash_len, KM_SLEEP);
626 error = veriexec_fp_calc(l, vp, lockstate, vfe, digest);
627 if (error) {
628 veriexec_file_report(vfe, "Fingerprint calculation error.",
629 name, NULL, REPORT_ALWAYS);
630 kmem_free(digest, vfe->ops->hash_len);
631 rw_exit(&vfe->lock);
632 if (lockstate == VERIEXEC_UNLOCKED)
633 rw_exit(&veriexec_op_lock);
634 return (error);
637 /* Compare fingerprint with loaded data. */
638 if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0)
639 vfe->status = FINGERPRINT_VALID;
640 else
641 vfe->status = FINGERPRINT_NOMATCH;
643 kmem_free(digest, vfe->ops->hash_len);
644 rw_downgrade(&vfe->lock);
647 if (!(vfe->type & flag)) {
648 veriexec_file_report(vfe, "Incorrect access type.", name, l,
649 REPORT_ALWAYS|REPORT_ALARM);
651 /* IPS mode: Enforce access type. */
652 if (veriexec_strict >= VERIEXEC_IPS) {
653 rw_exit(&vfe->lock);
654 if (lockstate == VERIEXEC_UNLOCKED)
655 rw_exit(&veriexec_op_lock);
656 return (EPERM);
660 out:
661 /* No entry in the veriexec tables. */
662 if (vfe == NULL) {
663 veriexec_file_report(NULL, "No entry.", name,
664 l, REPORT_VERBOSE);
666 if (lockstate == VERIEXEC_UNLOCKED)
667 rw_exit(&veriexec_op_lock);
669 * Lockdown mode: Deny access to non-monitored files.
670 * IPS mode: Deny execution of non-monitored files.
672 if ((veriexec_strict >= VERIEXEC_LOCKDOWN) ||
673 ((veriexec_strict >= VERIEXEC_IPS) &&
674 (flag != VERIEXEC_FILE)))
675 return (EPERM);
677 return (0);
680 switch (vfe->status) {
681 case FINGERPRINT_NOTEVAL:
682 /* Should not happen. */
683 rw_exit(&vfe->lock);
684 if (lockstate == VERIEXEC_UNLOCKED)
685 rw_exit(&veriexec_op_lock);
686 veriexec_file_report(vfe, "Not-evaluated status "
687 "post evaluation; inconsistency detected.", name,
688 NULL, REPORT_ALWAYS|REPORT_PANIC);
690 /*NOTREACHED*/
692 case FINGERPRINT_VALID:
693 /* Valid fingerprint. */
694 veriexec_file_report(vfe, "Match.", name, NULL,
695 REPORT_VERBOSE);
697 break;
699 case FINGERPRINT_NOMATCH:
700 /* Fingerprint mismatch. */
701 veriexec_file_report(vfe, "Mismatch.", name,
702 NULL, REPORT_ALWAYS|REPORT_ALARM);
704 /* IDS mode: Deny access on fingerprint mismatch. */
705 if (veriexec_strict >= VERIEXEC_IDS) {
706 rw_exit(&vfe->lock);
707 error = EPERM;
710 break;
712 default:
713 /* Should never happen. */
714 rw_exit(&vfe->lock);
715 if (lockstate == VERIEXEC_UNLOCKED)
716 rw_exit(&veriexec_op_lock);
717 veriexec_file_report(vfe, "Invalid status "
718 "post evaluation.", name, NULL, REPORT_ALWAYS|REPORT_PANIC);
721 if (lockstate == VERIEXEC_UNLOCKED)
722 rw_exit(&veriexec_op_lock);
723 return (error);
727 veriexec_verify(struct lwp *l, struct vnode *vp, const u_char *name, int flag,
728 bool *found)
730 struct veriexec_file_entry *vfe;
731 int r;
733 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
734 return 0;
736 r = veriexec_file_verify(l, vp, name, flag, VERIEXEC_UNLOCKED, &vfe);
738 if ((r == 0) && (vfe != NULL))
739 rw_exit(&vfe->lock);
741 if (found != NULL)
742 *found = (vfe != NULL) ? true : false;
744 return (r);
747 #ifdef notyet
749 * Evaluate per-page fingerprints.
752 veriexec_page_verify(struct veriexec_file_entry *vfe, struct vm_page *pg,
753 size_t idx, struct lwp *l)
755 void *ctx;
756 u_char *fp;
757 u_char *page_fp;
758 int error;
759 vaddr_t kva;
761 if (vfe->page_fp_status == PAGE_FP_NONE)
762 return (0);
764 if (vfe->page_fp_status == PAGE_FP_FAIL)
765 return (EPERM);
767 if (idx >= vfe->npages)
768 return (0);
770 ctx = kmem_alloc(vfe->ops->context_size, KM_SLEEP);
771 fp = kmem_alloc(vfe->ops->hash_len, KM_SLEEP);
772 kva = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
773 pmap_kenter_pa(kva, VM_PAGE_TO_PHYS(pg), VM_PROT_READ, 0);
774 pmap_update(pmap_kernel());
776 page_fp = (u_char *) vfe->page_fp + (vfe->ops->hash_len * idx);
777 (vfe->ops->init)(ctx);
778 (vfe->ops->update)(ctx, (void *) kva,
779 ((vfe->npages - 1) == idx) ? vfe->last_page_size
780 : PAGE_SIZE);
781 (vfe->ops->final)(fp, ctx);
783 pmap_kremove(kva, PAGE_SIZE);
784 pmap_update(pmap_kernel());
785 uvm_km_free(kernel_map, kva, PAGE_SIZE, UVM_KMF_VAONLY);
787 error = veriexec_fp_cmp(vfe->ops, page_fp, fp);
788 if (error) {
789 const char *msg;
791 if (veriexec_strict > VERIEXEC_LEARNING) {
792 msg = "Pages modified: Killing process.";
793 } else {
794 msg = "Pages modified.";
795 error = 0;
798 veriexec_file_report(msg, "[page_in]", l,
799 REPORT_ALWAYS|REPORT_ALARM);
801 if (error) {
802 ksiginfo_t ksi;
804 KSI_INIT(&ksi);
805 ksi.ksi_signo = SIGKILL;
806 ksi.ksi_code = SI_NOINFO;
807 ksi.ksi_pid = l->l_proc->p_pid;
808 ksi.ksi_uid = 0;
810 kpsignal(l->l_proc, &ksi, NULL);
814 kmem_free(ctx, vfe->ops->context_size);
815 kmem_free(fp, vfe->ops->hash_len);
817 return (error);
819 #endif /* notyet */
822 * Veriexec remove policy code.
825 veriexec_removechk(struct lwp *l, struct vnode *vp, const char *pathbuf)
827 struct veriexec_file_entry *vfe;
828 int error;
830 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
831 return 0;
833 rw_enter(&veriexec_op_lock, RW_READER);
835 vfe = veriexec_get(vp);
836 rw_exit(&veriexec_op_lock);
838 if (vfe == NULL) {
839 /* Lockdown mode: Deny access to non-monitored files. */
840 if (veriexec_strict >= VERIEXEC_LOCKDOWN)
841 return (EPERM);
843 return (0);
846 veriexec_file_report(vfe, "Remove request.", pathbuf, l,
847 REPORT_ALWAYS|REPORT_ALARM);
849 /* IDS mode: Deny removal of monitored files. */
850 if (veriexec_strict >= VERIEXEC_IDS)
851 error = EPERM;
852 else
853 error = veriexec_file_delete(l, vp);
856 return error;
860 * Veriexe rename policy.
862 * XXX: Once there's a way to hook after a successful rename, it would be
863 * XXX: nice to update vfe->filename to the new name if it's not NULL and
864 * XXX: the new name is absolute (ie., starts with a slash).
867 veriexec_renamechk(struct lwp *l, struct vnode *fromvp, const char *fromname,
868 struct vnode *tovp, const char *toname)
870 struct veriexec_file_entry *vfe, *tvfe;
872 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
873 return 0;
875 rw_enter(&veriexec_op_lock, RW_READER);
877 if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
878 log(LOG_ALERT, "Veriexec: Preventing rename of `%s' to "
879 "`%s', uid=%u, pid=%u: Lockdown mode.\n", fromname, toname,
880 kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
882 rw_exit(&veriexec_op_lock);
883 return (EPERM);
886 vfe = veriexec_get(fromvp);
887 tvfe = NULL;
888 if (tovp != NULL)
889 tvfe = veriexec_get(tovp);
891 if ((vfe != NULL) || (tvfe != NULL)) {
892 if (veriexec_strict >= VERIEXEC_IPS) {
893 log(LOG_ALERT, "Veriexec: Preventing rename of `%s' "
894 "to `%s', uid=%u, pid=%u: IPS mode, %s "
895 "monitored.\n", fromname, toname,
896 kauth_cred_geteuid(l->l_cred),
897 l->l_proc->p_pid, (vfe != NULL && tvfe != NULL) ?
898 "files" : "file");
900 rw_exit(&veriexec_op_lock);
901 return (EPERM);
905 * Monitored file is renamed; filename no longer relevant.
907 * XXX: We could keep the buffer, and when (and if) updating the
908 * XXX: filename post-rename, re-allocate it only if it's not
909 * XXX: big enough for the new filename.
911 if (vfe != NULL) {
912 /* XXXX get write lock on vfe here? */
914 VERIEXEC_RW_UPGRADE(&veriexec_op_lock);
915 /* once we have the op lock in write mode
916 * there should be no locks on any file
917 * entries so we can destroy the object.
920 if (vfe->filename_len > 0)
921 kmem_free(vfe->filename, vfe->filename_len);
923 vfe->filename = NULL;
924 vfe->filename_len = 0;
926 rw_downgrade(&veriexec_op_lock);
929 log(LOG_NOTICE, "Veriexec: %s file `%s' renamed to "
930 "%s file `%s', uid=%u, pid=%u.\n", (vfe != NULL) ?
931 "Monitored" : "Non-monitored", fromname, (tvfe != NULL) ?
932 "monitored" : "non-monitored", toname,
933 kauth_cred_geteuid(l->l_cred), l->l_proc->p_pid);
935 rw_exit(&veriexec_op_lock);
938 * Monitored file is overwritten. Remove the entry.
940 if (tvfe != NULL)
941 (void)veriexec_file_delete(l, tovp);
943 } else
944 rw_exit(&veriexec_op_lock);
946 return (0);
949 static void
950 veriexec_file_free(struct veriexec_file_entry *vfe)
952 if (vfe != NULL) {
953 if (vfe->fp != NULL)
954 kmem_free(vfe->fp, vfe->ops->hash_len);
955 if (vfe->page_fp != NULL)
956 kmem_free(vfe->page_fp, vfe->ops->hash_len);
957 if (vfe->filename != NULL)
958 kmem_free(vfe->filename, vfe->filename_len);
959 rw_destroy(&vfe->lock);
960 kmem_free(vfe, sizeof(*vfe));
964 static void
965 veriexec_file_purge(struct veriexec_file_entry *vfe, int have_lock)
967 if (vfe == NULL)
968 return;
970 if (have_lock == VERIEXEC_UNLOCKED)
971 rw_enter(&vfe->lock, RW_WRITER);
972 else
973 VERIEXEC_RW_UPGRADE(&vfe->lock);
975 vfe->status = FINGERPRINT_NOTEVAL;
976 if (have_lock == VERIEXEC_UNLOCKED)
977 rw_exit(&vfe->lock);
978 else
979 rw_downgrade(&vfe->lock);
982 static void
983 veriexec_file_purge_cb(struct veriexec_file_entry *vfe, void *cookie)
985 veriexec_file_purge(vfe, VERIEXEC_UNLOCKED);
989 * Invalidate a Veriexec file entry.
990 * XXX: This should be updated when per-page fingerprints are added.
992 void
993 veriexec_purge(struct vnode *vp)
996 rw_enter(&veriexec_op_lock, RW_READER);
997 veriexec_file_purge(veriexec_get(vp), VERIEXEC_UNLOCKED);
998 rw_exit(&veriexec_op_lock);
1002 * Enforce raw disk access policy.
1004 * IDS mode: Invalidate fingerprints on a mount if it's opened for writing.
1005 * IPS mode: Don't allow raw writing to disks we monitor.
1006 * Lockdown mode: Don't allow raw writing to all disks.
1008 * XXX: This is bogus. There's an obvious race condition between the time
1009 * XXX: the disk is open for writing, in which an attacker can access a
1010 * XXX: monitored file to get its signature cached again, and when the raw
1011 * XXX: file is overwritten on disk.
1012 * XXX:
1013 * XXX: To solve this, we need something like the following:
1014 * XXX: open raw disk:
1015 * XXX: - raise refcount,
1016 * XXX: - invalidate fingerprints,
1017 * XXX: - mark all entries for that disk with "no cache" flag
1018 * XXX:
1019 * XXX: veriexec_verify:
1020 * XXX: - if "no cache", don't cache evaluation result
1021 * XXX:
1022 * XXX: close raw disk:
1023 * XXX: - lower refcount,
1024 * XXX: - if refcount == 0, remove "no cache" flag from all entries
1026 static int
1027 veriexec_raw_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
1028 void *arg0, void *arg1, void *arg2, void *arg3)
1030 int result;
1031 enum kauth_device_req req;
1032 struct veriexec_table_entry *vte;
1034 result = KAUTH_RESULT_DENY;
1035 req = (enum kauth_device_req)arg0;
1037 switch (action) {
1038 case KAUTH_DEVICE_RAWIO_SPEC: {
1039 struct vnode *vp, *bvp;
1040 int error;
1042 if (req == KAUTH_REQ_DEVICE_RAWIO_SPEC_READ) {
1043 result = KAUTH_RESULT_DEFER;
1044 break;
1047 vp = arg1;
1048 KASSERT(vp != NULL);
1050 /* Handle /dev/mem and /dev/kmem. */
1051 if (iskmemvp(vp)) {
1052 if (veriexec_strict < VERIEXEC_IPS)
1053 result = KAUTH_RESULT_DEFER;
1055 break;
1058 error = rawdev_mounted(vp, &bvp);
1059 if (error == EINVAL) {
1060 result = KAUTH_RESULT_DEFER;
1061 break;
1065 * XXX: See vfs_mountedon() comment in rawdev_mounted().
1067 vte = veriexec_table_lookup(bvp->v_mount);
1068 if (vte == NULL) {
1069 result = KAUTH_RESULT_DEFER;
1070 break;
1073 switch (veriexec_strict) {
1074 case VERIEXEC_LEARNING:
1075 case VERIEXEC_IDS:
1076 result = KAUTH_RESULT_DEFER;
1078 rw_enter(&veriexec_op_lock, RW_WRITER);
1079 fileassoc_table_run(bvp->v_mount, veriexec_hook,
1080 (fileassoc_cb_t)veriexec_file_purge_cb, NULL);
1081 rw_exit(&veriexec_op_lock);
1083 break;
1084 case VERIEXEC_IPS:
1085 result = KAUTH_RESULT_DENY;
1086 break;
1087 case VERIEXEC_LOCKDOWN:
1088 result = KAUTH_RESULT_DENY;
1089 break;
1092 break;
1095 case KAUTH_DEVICE_RAWIO_PASSTHRU:
1096 /* XXX What can we do here? */
1097 if (veriexec_strict < VERIEXEC_IPS)
1098 result = KAUTH_RESULT_DEFER;
1100 break;
1102 default:
1103 result = KAUTH_RESULT_DEFER;
1104 break;
1107 return (result);
1111 * Create a new Veriexec table.
1113 static struct veriexec_table_entry *
1114 veriexec_table_add(struct lwp *l, struct mount *mp)
1116 struct veriexec_table_entry *vte;
1117 u_char buf[16];
1119 vte = kmem_zalloc(sizeof(*vte), KM_SLEEP);
1120 mount_setspecific(mp, veriexec_mountspecific_key, vte);
1122 snprintf(buf, sizeof(buf), "table%u", veriexec_tablecount++);
1123 sysctl_createv(NULL, 0, &veriexec_count_node, &vte->vte_node,
1124 0, CTLTYPE_NODE, buf, NULL, NULL, 0, NULL,
1125 0, CTL_CREATE, CTL_EOL);
1127 sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1128 CTLFLAG_READONLY, CTLTYPE_STRING, "mntpt",
1129 NULL, NULL, 0, mp->mnt_stat.f_mntonname,
1130 0, CTL_CREATE, CTL_EOL);
1131 sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1132 CTLFLAG_READONLY, CTLTYPE_STRING, "fstype",
1133 NULL, NULL, 0, mp->mnt_stat.f_fstypename,
1134 0, CTL_CREATE, CTL_EOL);
1135 sysctl_createv(NULL, 0, &vte->vte_node, NULL,
1136 CTLFLAG_READONLY, CTLTYPE_QUAD, "nentries",
1137 NULL, NULL, 0, &vte->vte_count, 0, CTL_CREATE, CTL_EOL);
1139 return (vte);
1143 * Add a file to be monitored by Veriexec.
1145 * Expected elements in dict: file, fp, fp-type, entry-type.
1148 veriexec_file_add(struct lwp *l, prop_dictionary_t dict)
1150 struct veriexec_table_entry *vte;
1151 struct veriexec_file_entry *vfe = NULL, *hh;
1152 struct vnode *vp;
1153 const char *file, *fp_type;
1154 int error;
1156 if (!prop_dictionary_get_cstring_nocopy(dict, "file", &file))
1157 return (EINVAL);
1159 error = namei_simple_kernel(file, NSM_FOLLOW_NOEMULROOT, &vp);
1160 if (error)
1161 return (error);
1163 /* Add only regular files. */
1164 if (vp->v_type != VREG) {
1165 log(LOG_ERR, "Veriexec: Not adding `%s': Not a regular file.\n",
1166 file);
1168 error = EBADF;
1170 goto out;
1173 vfe = kmem_zalloc(sizeof(*vfe), KM_SLEEP);
1175 rw_init(&vfe->lock);
1177 /* Lookup fingerprint hashing algorithm. */
1178 fp_type = prop_string_cstring_nocopy(prop_dictionary_get(dict,
1179 "fp-type"));
1180 if ((vfe->ops = veriexec_fpops_lookup(fp_type)) == NULL) {
1181 log(LOG_ERR, "Veriexec: Invalid or unknown fingerprint type "
1182 "`%s' for file `%s'.\n", fp_type, file);
1184 error = EOPNOTSUPP;
1186 goto out;
1189 if (prop_data_size(prop_dictionary_get(dict, "fp")) !=
1190 vfe->ops->hash_len) {
1191 log(LOG_ERR, "Veriexec: Bad fingerprint length for `%s'.\n",
1192 file);
1194 error = EINVAL;
1196 goto out;
1199 vfe->fp = kmem_alloc(vfe->ops->hash_len, KM_SLEEP);
1200 memcpy(vfe->fp, prop_data_data_nocopy(prop_dictionary_get(dict, "fp")),
1201 vfe->ops->hash_len);
1203 rw_enter(&veriexec_op_lock, RW_WRITER);
1206 * See if we already have an entry for this file. If we do, then
1207 * let the user know and silently pretend to succeed.
1209 hh = veriexec_get(vp);
1210 if (hh != NULL) {
1211 bool fp_mismatch;
1213 if (strcmp(vfe->ops->type, fp_type) ||
1214 memcmp(hh->fp, vfe->fp, hh->ops->hash_len))
1215 fp_mismatch = true;
1216 else
1217 fp_mismatch = false;
1219 if ((veriexec_verbose >= 1) || fp_mismatch)
1220 log(LOG_NOTICE, "Veriexec: Duplicate entry for `%s' "
1221 "ignored. (%s fingerprint)\n", file,
1222 fp_mismatch ? "different" : "same");
1224 veriexec_file_free(vfe);
1226 /* XXX Should this be EEXIST if fp_mismatch is true? */
1227 error = 0;
1229 goto unlock_out;
1232 /* Continue entry initialization. */
1233 if (prop_dictionary_get_uint8(dict, "entry-type", &vfe->type) == FALSE)
1234 vfe->type = 0;
1235 else {
1236 uint8_t extra_flags;
1238 extra_flags = vfe->type & ~(VERIEXEC_DIRECT |
1239 VERIEXEC_INDIRECT | VERIEXEC_FILE | VERIEXEC_UNTRUSTED);
1240 if (extra_flags) {
1241 log(LOG_NOTICE, "Veriexec: Contaminated flags `0x%x' "
1242 "for `%s', skipping.\n", extra_flags, file);
1244 error = EINVAL;
1246 goto unlock_out;
1249 if (!(vfe->type & (VERIEXEC_DIRECT | VERIEXEC_INDIRECT |
1250 VERIEXEC_FILE)))
1251 vfe->type |= VERIEXEC_DIRECT;
1253 vfe->status = FINGERPRINT_NOTEVAL;
1254 if (prop_bool_true(prop_dictionary_get(dict, "keep-filename"))) {
1255 vfe->filename_len = strlen(file) + 1;
1256 vfe->filename = kmem_alloc(vfe->filename_len, KM_SLEEP);
1257 strlcpy(vfe->filename, file, vfe->filename_len);
1258 } else
1259 vfe->filename = NULL;
1261 vfe->page_fp = NULL;
1262 vfe->page_fp_status = PAGE_FP_NONE;
1263 vfe->npages = 0;
1264 vfe->last_page_size = 0;
1266 vte = veriexec_table_lookup(vp->v_mount);
1267 if (vte == NULL)
1268 vte = veriexec_table_add(l, vp->v_mount);
1270 /* XXX if we bail below this, we might want to gc newly created vtes. */
1272 error = fileassoc_add(vp, veriexec_hook, vfe);
1273 if (error)
1274 goto unlock_out;
1276 vte->vte_count++;
1278 if (prop_bool_true(prop_dictionary_get(dict, "eval-on-load")) ||
1279 (vfe->type & VERIEXEC_UNTRUSTED)) {
1280 u_char *digest;
1282 digest = kmem_zalloc(vfe->ops->hash_len, KM_SLEEP);
1284 error = veriexec_fp_calc(l, vp, VERIEXEC_UNLOCKED,
1285 vfe, digest);
1286 if (error) {
1287 kmem_free(digest, vfe->ops->hash_len);
1288 goto unlock_out;
1291 if (veriexec_fp_cmp(vfe->ops, vfe->fp, digest) == 0)
1292 vfe->status = FINGERPRINT_VALID;
1293 else
1294 vfe->status = FINGERPRINT_NOMATCH;
1296 kmem_free(digest, vfe->ops->hash_len);
1299 veriexec_file_report(NULL, "New entry.", file, NULL, REPORT_DEBUG);
1300 veriexec_bypass = 0;
1302 unlock_out:
1303 rw_exit(&veriexec_op_lock);
1305 out:
1306 vrele(vp);
1307 if (error)
1308 veriexec_file_free(vfe);
1310 return (error);
1314 veriexec_table_delete(struct lwp *l, struct mount *mp) {
1315 struct veriexec_table_entry *vte;
1317 vte = veriexec_table_lookup(mp);
1318 if (vte == NULL)
1319 return (ENOENT);
1321 veriexec_mountspecific_dtor(vte);
1322 mount_setspecific(mp, veriexec_mountspecific_key, NULL);
1324 return (fileassoc_table_clear(mp, veriexec_hook));
1328 veriexec_file_delete(struct lwp *l, struct vnode *vp) {
1329 struct veriexec_table_entry *vte;
1330 int error;
1332 vte = veriexec_table_lookup(vp->v_mount);
1333 if (vte == NULL)
1334 return (ENOENT);
1336 rw_enter(&veriexec_op_lock, RW_WRITER);
1337 error = fileassoc_clear(vp, veriexec_hook);
1338 rw_exit(&veriexec_op_lock);
1339 if (!error)
1340 vte->vte_count--;
1342 return (error);
1346 * Convert Veriexec entry data to a dictionary readable by userland tools.
1348 static void
1349 veriexec_file_convert(struct veriexec_file_entry *vfe, prop_dictionary_t rdict)
1351 if (vfe->filename)
1352 prop_dictionary_set(rdict, "file",
1353 prop_string_create_cstring(vfe->filename));
1354 prop_dictionary_set_uint8(rdict, "entry-type", vfe->type);
1355 prop_dictionary_set_uint8(rdict, "status", vfe->status);
1356 prop_dictionary_set(rdict, "fp-type",
1357 prop_string_create_cstring(vfe->ops->type));
1358 prop_dictionary_set(rdict, "fp",
1359 prop_data_create_data(vfe->fp, vfe->ops->hash_len));
1363 veriexec_convert(struct vnode *vp, prop_dictionary_t rdict)
1365 struct veriexec_file_entry *vfe;
1367 rw_enter(&veriexec_op_lock, RW_READER);
1369 vfe = veriexec_get(vp);
1370 if (vfe == NULL) {
1371 rw_exit(&veriexec_op_lock);
1372 return (ENOENT);
1375 rw_enter(&vfe->lock, RW_READER);
1376 veriexec_file_convert(vfe, rdict);
1378 rw_exit(&vfe->lock);
1379 rw_exit(&veriexec_op_lock);
1380 return (0);
1384 veriexec_unmountchk(struct mount *mp)
1386 int error;
1388 if ((veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
1389 || doing_shutdown)
1390 return (0);
1392 rw_enter(&veriexec_op_lock, RW_READER);
1394 switch (veriexec_strict) {
1395 case VERIEXEC_LEARNING:
1396 error = 0;
1397 break;
1399 case VERIEXEC_IDS:
1400 if (veriexec_table_lookup(mp) != NULL) {
1401 log(LOG_INFO, "Veriexec: IDS mode, allowing unmount "
1402 "of \"%s\".\n", mp->mnt_stat.f_mntonname);
1405 error = 0;
1406 break;
1408 case VERIEXEC_IPS: {
1409 struct veriexec_table_entry *vte;
1411 vte = veriexec_table_lookup(mp);
1412 if ((vte != NULL) && (vte->vte_count > 0)) {
1413 log(LOG_ALERT, "Veriexec: IPS mode, preventing"
1414 " unmount of \"%s\" with monitored files.\n",
1415 mp->mnt_stat.f_mntonname);
1417 error = EPERM;
1418 } else
1419 error = 0;
1420 break;
1423 case VERIEXEC_LOCKDOWN:
1424 default:
1425 log(LOG_ALERT, "Veriexec: Lockdown mode, preventing unmount "
1426 "of \"%s\".\n", mp->mnt_stat.f_mntonname);
1427 error = EPERM;
1428 break;
1431 rw_exit(&veriexec_op_lock);
1432 return (error);
1436 veriexec_openchk(struct lwp *l, struct vnode *vp, const char *path, int fmode)
1438 struct veriexec_file_entry *vfe = NULL;
1439 int error = 0;
1441 if (veriexec_bypass && (veriexec_strict == VERIEXEC_LEARNING))
1442 return 0;
1444 if (vp == NULL) {
1445 /* If no creation requested, let this fail normally. */
1446 if (!(fmode & O_CREAT))
1447 goto out;
1449 /* Lockdown mode: Prevent creation of new files. */
1450 if (veriexec_strict >= VERIEXEC_LOCKDOWN) {
1451 log(LOG_ALERT, "Veriexec: Preventing new file "
1452 "creation in `%s'.\n", path);
1453 error = EPERM;
1456 goto out;
1459 rw_enter(&veriexec_op_lock, RW_READER);
1460 error = veriexec_file_verify(l, vp, path, VERIEXEC_FILE,
1461 VERIEXEC_LOCKED, &vfe);
1463 if (error) {
1464 rw_exit(&veriexec_op_lock);
1465 goto out;
1468 if ((vfe != NULL) && ((fmode & FWRITE) || (fmode & O_TRUNC))) {
1469 veriexec_file_report(vfe, "Write access request.", path, l,
1470 REPORT_ALWAYS | REPORT_ALARM);
1472 /* IPS mode: Deny write access to monitored files. */
1473 if (veriexec_strict >= VERIEXEC_IPS)
1474 error = EPERM;
1475 else
1476 veriexec_file_purge(vfe, VERIEXEC_LOCKED);
1479 if (vfe != NULL)
1480 rw_exit(&vfe->lock);
1482 rw_exit(&veriexec_op_lock);
1483 out:
1484 return (error);
1487 static void
1488 veriexec_file_dump(struct veriexec_file_entry *vfe, prop_array_t entries)
1490 prop_dictionary_t entry;
1492 /* If we don't have a filename, this is meaningless. */
1493 if (vfe->filename == NULL)
1494 return;
1496 entry = prop_dictionary_create();
1498 veriexec_file_convert(vfe, entry);
1500 prop_array_add(entries, entry);
1504 veriexec_dump(struct lwp *l, prop_array_t rarray)
1506 struct mount *mp, *nmp;
1508 mutex_enter(&mountlist_lock);
1509 for (mp = CIRCLEQ_FIRST(&mountlist); mp != (void *)&mountlist;
1510 mp = nmp) {
1511 /* If it fails, the file-system is [being] unmounted. */
1512 if (vfs_busy(mp, &nmp) != 0)
1513 continue;
1515 fileassoc_table_run(mp, veriexec_hook,
1516 (fileassoc_cb_t)veriexec_file_dump, rarray);
1518 vfs_unbusy(mp, false, &nmp);
1520 mutex_exit(&mountlist_lock);
1522 return (0);
1526 veriexec_flush(struct lwp *l)
1528 struct mount *mp, *nmp;
1529 int error = 0;
1531 mutex_enter(&mountlist_lock);
1532 for (mp = CIRCLEQ_FIRST(&mountlist); mp != (void *)&mountlist;
1533 mp = nmp) {
1534 int lerror;
1536 /* If it fails, the file-system is [being] unmounted. */
1537 if (vfs_busy(mp, &nmp) != 0)
1538 continue;
1540 lerror = veriexec_table_delete(l, mp);
1541 if (lerror && lerror != ENOENT)
1542 error = lerror;
1544 vfs_unbusy(mp, false, &nmp);
1546 mutex_exit(&mountlist_lock);
1548 return (error);