1 /* $NetBSD: kern_verifiedexec.c,v 1.120 2009/12/28 02:35:20 elad Exp $ */
4 * Copyright (c) 2005, 2006 Elad Efrat <elad@NetBSD.org>
5 * Copyright (c) 2005, 2006 Brett Lymn <blymn@NetBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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>
39 #include <sys/vnode.h>
40 #include <sys/namei.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>
56 # include <sys/sha1.h>
57 # include <sys/sha2.h>
58 # include <sys/rmd160.h>
61 #include <uvm/uvm_extern.h>
62 #include <sys/fileassoc.h>
63 #include <sys/kauth.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
{
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
;
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
142 static krwlock_t veriexec_op_lock
;
145 * Sysctl helper routine for Veriexec.
148 sysctl_kern_veriexec(SYSCTLFN_ARGS
)
151 int *var
= NULL
, raise_only
= 0;
152 struct sysctlnode node
;
156 if (strcmp(rnode
->sysctl_name
, "strict") == 0) {
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
)));
169 node
.sysctl_data
= &newval
;
170 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
171 if (error
|| newp
== NULL
) {
175 if (raise_only
&& (newval
< *var
))
183 SYSCTL_SETUP(sysctl_kern_veriexec_setup
, "sysctl kern.veriexec setup")
185 const struct sysctlnode
*rnode
= NULL
;
187 sysctl_createv(clog
, 0, NULL
, &rnode
,
189 CTLTYPE_NODE
, "kern", NULL
,
193 sysctl_createv(clog
, 0, &rnode
, &rnode
,
195 CTLTYPE_NODE
, "veriexec",
196 SYSCTL_DESCR("Veriexec"),
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
,
214 CTLTYPE_STRING
, "algorithms",
215 SYSCTL_DESCR("Veriexec supported hashing "
217 sysctl_kern_veriexec
, 0, NULL
, 0,
218 CTL_CREATE
, CTL_EOL
);
219 sysctl_createv(clog
, 0, &rnode
, &veriexec_count_node
,
221 CTLTYPE_NODE
, "count",
222 SYSCTL_DESCR("Number of fingerprints on mount(s)"),
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
))
242 if (veriexec_fpops_lookup(fp_type
) != NULL
)
245 ops
= kmem_alloc(sizeof(*ops
), KM_SLEEP
);
248 ops
->hash_len
= hash_len
;
249 ops
->context_size
= ctx_size
;
251 ops
->update
= update
;
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) <
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
);
293 veriexec_mountspecific_dtor(void *v
)
295 struct veriexec_table_entry
*vte
= v
;
300 sysctl_free(__UNCONST(vte
->vte_node
));
301 veriexec_tablecount
--;
302 kmem_free(vte
, sizeof(*vte
));
306 * Initialise Veriexec.
313 /* Register a fileassoc for Veriexec. */
314 error
= fileassoc_register("veriexec",
315 (fileassoc_cleanup_cb_t
)veriexec_file_free
, &veriexec_hook
);
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
) ==
322 panic("Veriexec: Can't listen on device scope");
324 error
= mount_specific_key_create(&veriexec_mountspecific_key
,
325 veriexec_mountspecific_dtor
);
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 */
368 static struct veriexec_fpops
*
369 veriexec_fpops_lookup(const char *name
)
371 struct veriexec_fpops
*ops
;
376 LIST_FOREACH(ops
, &veriexec_fpops_list
, entries
) {
377 if (strcasecmp(name
, ops
->type
) == 0)
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.
391 veriexec_fp_calc(struct lwp
*l
, struct vnode
*vp
, int lock_state
,
392 struct veriexec_file_entry
*vfe
, u_char
*fp
)
395 void *ctx
, *page_ctx
;
396 u_char
*buf
, *page_fp
;
398 size_t resid
, npages
;
399 int error
, do_perpage
, pagen
;
401 error
= VOP_GETATTR(vp
, &va
, l
->l_cred
);
405 #ifdef notyet /* XXX - for now */
406 if ((vfe
->type
& VERIEXEC_UNTRUSTED
) &&
407 (vfe
->page_fp_status
== PAGE_FP_NONE
))
413 ctx
= kmem_alloc(vfe
->ops
->context_size
, KM_SLEEP
);
414 buf
= kmem_alloc(PAGE_SIZE
, KM_SLEEP
);
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
);
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
,
437 ((lock_state
== VERIEXEC_LOCKED
)?
439 l
->l_cred
, &resid
, NULL
);
443 kmem_free(vfe
->page_fp
,
444 vfe
->ops
->hash_len
* npages
);
451 (vfe
->ops
->update
)(ctx
, buf
, (unsigned int) len
);
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) {
461 printf("hash for page %d: ", pagen
);
462 for (i
= 0; i
< vfe
->ops
->hash_len
; i
++)
463 printf("%02x", page_fp
[i
]);
467 page_fp
+= vfe
->ops
->hash_len
;
471 if (len
!= PAGE_SIZE
)
475 (vfe
->ops
->final
)(fp
, ctx
);
478 vfe
->last_page_size
= len
;
479 vfe
->page_fp_status
= PAGE_FP_READY
;
480 vfe
->npages
= npages
;
485 kmem_free(page_ctx
, vfe
->ops
->context_size
);
487 kmem_free(ctx
, vfe
->ops
->context_size
);
488 kmem_free(buf
, PAGE_SIZE
);
493 /* Compare two fingerprints of the same type. */
495 veriexec_fp_cmp(struct veriexec_fpops
*ops
, u_char
*fp1
, u_char
*fp2
)
497 if (veriexec_verbose
>= 2) {
500 printf("comparing hashes...\n");
502 for (i
= 0; i
< ops
->hash_len
; i
++) {
503 printf("%02x", fp1
[i
]);
506 for (i
= 0; i
< ops
->hash_len
; i
++) {
507 printf("%02x", fp2
[i
]);
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 */
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
));
532 veriexec_lookup(struct vnode
*vp
)
534 return (veriexec_get(vp
) == NULL
? false : true);
538 * Routine for maintaining mostly consistent message formats in Veriexec.
541 veriexec_file_report(struct veriexec_file_entry
*vfe
, const u_char
*msg
,
542 const u_char
*filename
, struct lwp
*l
, int f
)
547 if (vfe
!= NULL
&& vfe
->filename
!= NULL
)
548 filename
= vfe
->filename
;
550 if (filename
== NULL
)
553 if (((f
& REPORT_LOGMASK
) >> 1) <= veriexec_verbose
) {
554 if (!(f
& REPORT_ALARM
) || (l
== NULL
))
555 log(LOG_NOTICE
, "Veriexec: %s [%s]\n", msg
,
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
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
;
585 #define VFE_NEEDS_EVAL(vfe) ((vfe->status == FINGERPRINT_NOTEVAL) || \
586 (vfe->type & VERIEXEC_UNTRUSTED))
591 if (vp
->v_type
!= VREG
)
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
);
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
);
617 rw_enter(&vfe
->lock
, RW_READER
);
619 /* Evaluate fingerprint if needed. */
620 if (VFE_NEEDS_EVAL(vfe
)) {
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
);
628 veriexec_file_report(vfe
, "Fingerprint calculation error.",
629 name
, NULL
, REPORT_ALWAYS
);
630 kmem_free(digest
, vfe
->ops
->hash_len
);
632 if (lockstate
== VERIEXEC_UNLOCKED
)
633 rw_exit(&veriexec_op_lock
);
637 /* Compare fingerprint with loaded data. */
638 if (veriexec_fp_cmp(vfe
->ops
, vfe
->fp
, digest
) == 0)
639 vfe
->status
= FINGERPRINT_VALID
;
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
) {
654 if (lockstate
== VERIEXEC_UNLOCKED
)
655 rw_exit(&veriexec_op_lock
);
661 /* No entry in the veriexec tables. */
663 veriexec_file_report(NULL
, "No entry.", name
,
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
)))
680 switch (vfe
->status
) {
681 case FINGERPRINT_NOTEVAL
:
682 /* Should not happen. */
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
);
692 case FINGERPRINT_VALID
:
693 /* Valid fingerprint. */
694 veriexec_file_report(vfe
, "Match.", name
, NULL
,
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
) {
713 /* Should never happen. */
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
);
727 veriexec_verify(struct lwp
*l
, struct vnode
*vp
, const u_char
*name
, int flag
,
730 struct veriexec_file_entry
*vfe
;
733 if (veriexec_bypass
&& (veriexec_strict
== VERIEXEC_LEARNING
))
736 r
= veriexec_file_verify(l
, vp
, name
, flag
, VERIEXEC_UNLOCKED
, &vfe
);
738 if ((r
== 0) && (vfe
!= NULL
))
742 *found
= (vfe
!= NULL
) ? true : false;
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
)
761 if (vfe
->page_fp_status
== PAGE_FP_NONE
)
764 if (vfe
->page_fp_status
== PAGE_FP_FAIL
)
767 if (idx
>= vfe
->npages
)
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
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
);
791 if (veriexec_strict
> VERIEXEC_LEARNING
) {
792 msg
= "Pages modified: Killing process.";
794 msg
= "Pages modified.";
798 veriexec_file_report(msg
, "[page_in]", l
,
799 REPORT_ALWAYS
|REPORT_ALARM
);
805 ksi
.ksi_signo
= SIGKILL
;
806 ksi
.ksi_code
= SI_NOINFO
;
807 ksi
.ksi_pid
= l
->l_proc
->p_pid
;
810 kpsignal(l
->l_proc
, &ksi
, NULL
);
814 kmem_free(ctx
, vfe
->ops
->context_size
);
815 kmem_free(fp
, vfe
->ops
->hash_len
);
822 * Veriexec remove policy code.
825 veriexec_removechk(struct lwp
*l
, struct vnode
*vp
, const char *pathbuf
)
827 struct veriexec_file_entry
*vfe
;
830 if (veriexec_bypass
&& (veriexec_strict
== VERIEXEC_LEARNING
))
833 rw_enter(&veriexec_op_lock
, RW_READER
);
835 vfe
= veriexec_get(vp
);
836 rw_exit(&veriexec_op_lock
);
839 /* Lockdown mode: Deny access to non-monitored files. */
840 if (veriexec_strict
>= VERIEXEC_LOCKDOWN
)
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
)
853 error
= veriexec_file_delete(l
, vp
);
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
))
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
);
886 vfe
= veriexec_get(fromvp
);
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
) ?
900 rw_exit(&veriexec_op_lock
);
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.
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.
941 (void)veriexec_file_delete(l
, tovp
);
944 rw_exit(&veriexec_op_lock
);
950 veriexec_file_free(struct veriexec_file_entry
*vfe
)
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
));
965 veriexec_file_purge(struct veriexec_file_entry
*vfe
, int have_lock
)
970 if (have_lock
== VERIEXEC_UNLOCKED
)
971 rw_enter(&vfe
->lock
, RW_WRITER
);
973 VERIEXEC_RW_UPGRADE(&vfe
->lock
);
975 vfe
->status
= FINGERPRINT_NOTEVAL
;
976 if (have_lock
== VERIEXEC_UNLOCKED
)
979 rw_downgrade(&vfe
->lock
);
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.
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.
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
1019 * XXX: veriexec_verify:
1020 * XXX: - if "no cache", don't cache evaluation result
1022 * XXX: close raw disk:
1023 * XXX: - lower refcount,
1024 * XXX: - if refcount == 0, remove "no cache" flag from all entries
1027 veriexec_raw_cb(kauth_cred_t cred
, kauth_action_t action
, void *cookie
,
1028 void *arg0
, void *arg1
, void *arg2
, void *arg3
)
1031 enum kauth_device_req req
;
1032 struct veriexec_table_entry
*vte
;
1034 result
= KAUTH_RESULT_DENY
;
1035 req
= (enum kauth_device_req
)arg0
;
1038 case KAUTH_DEVICE_RAWIO_SPEC
: {
1039 struct vnode
*vp
, *bvp
;
1042 if (req
== KAUTH_REQ_DEVICE_RAWIO_SPEC_READ
) {
1043 result
= KAUTH_RESULT_DEFER
;
1048 KASSERT(vp
!= NULL
);
1050 /* Handle /dev/mem and /dev/kmem. */
1052 if (veriexec_strict
< VERIEXEC_IPS
)
1053 result
= KAUTH_RESULT_DEFER
;
1058 error
= rawdev_mounted(vp
, &bvp
);
1059 if (error
== EINVAL
) {
1060 result
= KAUTH_RESULT_DEFER
;
1065 * XXX: See vfs_mountedon() comment in rawdev_mounted().
1067 vte
= veriexec_table_lookup(bvp
->v_mount
);
1069 result
= KAUTH_RESULT_DEFER
;
1073 switch (veriexec_strict
) {
1074 case VERIEXEC_LEARNING
:
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
);
1085 result
= KAUTH_RESULT_DENY
;
1087 case VERIEXEC_LOCKDOWN
:
1088 result
= KAUTH_RESULT_DENY
;
1095 case KAUTH_DEVICE_RAWIO_PASSTHRU
:
1096 /* XXX What can we do here? */
1097 if (veriexec_strict
< VERIEXEC_IPS
)
1098 result
= KAUTH_RESULT_DEFER
;
1103 result
= KAUTH_RESULT_DEFER
;
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
;
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
);
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
;
1153 const char *file
, *fp_type
;
1156 if (!prop_dictionary_get_cstring_nocopy(dict
, "file", &file
))
1159 error
= namei_simple_kernel(file
, NSM_FOLLOW_NOEMULROOT
, &vp
);
1163 /* Add only regular files. */
1164 if (vp
->v_type
!= VREG
) {
1165 log(LOG_ERR
, "Veriexec: Not adding `%s': Not a regular file.\n",
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
,
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
);
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",
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
);
1213 if (strcmp(vfe
->ops
->type
, fp_type
) ||
1214 memcmp(hh
->fp
, vfe
->fp
, hh
->ops
->hash_len
))
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? */
1232 /* Continue entry initialization. */
1233 if (prop_dictionary_get_uint8(dict
, "entry-type", &vfe
->type
) == FALSE
)
1236 uint8_t extra_flags
;
1238 extra_flags
= vfe
->type
& ~(VERIEXEC_DIRECT
|
1239 VERIEXEC_INDIRECT
| VERIEXEC_FILE
| VERIEXEC_UNTRUSTED
);
1241 log(LOG_NOTICE
, "Veriexec: Contaminated flags `0x%x' "
1242 "for `%s', skipping.\n", extra_flags
, file
);
1249 if (!(vfe
->type
& (VERIEXEC_DIRECT
| VERIEXEC_INDIRECT
|
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
);
1259 vfe
->filename
= NULL
;
1261 vfe
->page_fp
= NULL
;
1262 vfe
->page_fp_status
= PAGE_FP_NONE
;
1264 vfe
->last_page_size
= 0;
1266 vte
= veriexec_table_lookup(vp
->v_mount
);
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
);
1278 if (prop_bool_true(prop_dictionary_get(dict
, "eval-on-load")) ||
1279 (vfe
->type
& VERIEXEC_UNTRUSTED
)) {
1282 digest
= kmem_zalloc(vfe
->ops
->hash_len
, KM_SLEEP
);
1284 error
= veriexec_fp_calc(l
, vp
, VERIEXEC_UNLOCKED
,
1287 kmem_free(digest
, vfe
->ops
->hash_len
);
1291 if (veriexec_fp_cmp(vfe
->ops
, vfe
->fp
, digest
) == 0)
1292 vfe
->status
= FINGERPRINT_VALID
;
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;
1303 rw_exit(&veriexec_op_lock
);
1308 veriexec_file_free(vfe
);
1314 veriexec_table_delete(struct lwp
*l
, struct mount
*mp
) {
1315 struct veriexec_table_entry
*vte
;
1317 vte
= veriexec_table_lookup(mp
);
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
;
1332 vte
= veriexec_table_lookup(vp
->v_mount
);
1336 rw_enter(&veriexec_op_lock
, RW_WRITER
);
1337 error
= fileassoc_clear(vp
, veriexec_hook
);
1338 rw_exit(&veriexec_op_lock
);
1346 * Convert Veriexec entry data to a dictionary readable by userland tools.
1349 veriexec_file_convert(struct veriexec_file_entry
*vfe
, prop_dictionary_t rdict
)
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
);
1371 rw_exit(&veriexec_op_lock
);
1375 rw_enter(&vfe
->lock
, RW_READER
);
1376 veriexec_file_convert(vfe
, rdict
);
1378 rw_exit(&vfe
->lock
);
1379 rw_exit(&veriexec_op_lock
);
1384 veriexec_unmountchk(struct mount
*mp
)
1388 if ((veriexec_bypass
&& (veriexec_strict
== VERIEXEC_LEARNING
))
1392 rw_enter(&veriexec_op_lock
, RW_READER
);
1394 switch (veriexec_strict
) {
1395 case VERIEXEC_LEARNING
:
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
);
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
);
1423 case VERIEXEC_LOCKDOWN
:
1425 log(LOG_ALERT
, "Veriexec: Lockdown mode, preventing unmount "
1426 "of \"%s\".\n", mp
->mnt_stat
.f_mntonname
);
1431 rw_exit(&veriexec_op_lock
);
1436 veriexec_openchk(struct lwp
*l
, struct vnode
*vp
, const char *path
, int fmode
)
1438 struct veriexec_file_entry
*vfe
= NULL
;
1441 if (veriexec_bypass
&& (veriexec_strict
== VERIEXEC_LEARNING
))
1445 /* If no creation requested, let this fail normally. */
1446 if (!(fmode
& O_CREAT
))
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
);
1459 rw_enter(&veriexec_op_lock
, RW_READER
);
1460 error
= veriexec_file_verify(l
, vp
, path
, VERIEXEC_FILE
,
1461 VERIEXEC_LOCKED
, &vfe
);
1464 rw_exit(&veriexec_op_lock
);
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
)
1476 veriexec_file_purge(vfe
, VERIEXEC_LOCKED
);
1480 rw_exit(&vfe
->lock
);
1482 rw_exit(&veriexec_op_lock
);
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
)
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
;
1511 /* If it fails, the file-system is [being] unmounted. */
1512 if (vfs_busy(mp
, &nmp
) != 0)
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
);
1526 veriexec_flush(struct lwp
*l
)
1528 struct mount
*mp
, *nmp
;
1531 mutex_enter(&mountlist_lock
);
1532 for (mp
= CIRCLEQ_FIRST(&mountlist
); mp
!= (void *)&mountlist
;
1536 /* If it fails, the file-system is [being] unmounted. */
1537 if (vfs_busy(mp
, &nmp
) != 0)
1540 lerror
= veriexec_table_delete(l
, mp
);
1541 if (lerror
&& lerror
!= ENOENT
)
1544 vfs_unbusy(mp
, false, &nmp
);
1546 mutex_exit(&mountlist_lock
);