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