1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2011, 2012, 2013, 2015, 2016, 2019, Juniper Networks, Inc.
5 * All rights reserved.
6 *
7 * Originally derived from:
8 * $NetBSD: kern_verifiedexec.c,v 1.7 2003/11/18 13:13:03 martin Exp $
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33
34 #include "opt_mac.h"
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/exec.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/mutex.h>
42 #include <sys/proc.h>
43 #include <sys/sbuf.h>
44 #ifdef MAC_VERIEXEC_DEBUG
45 #include <sys/syslog.h>
46 #endif
47 #include <sys/vnode.h>
48
49 #include "mac_veriexec.h"
50 #include "mac_veriexec_internal.h"
51
52 /**
53 * @brief per-device meta-data storage
54 */
55 struct veriexec_dev_list {
56 dev_t fsid; /**< file system identifier of the mount point */
57 LIST_HEAD(filehead, mac_veriexec_file_info) file_head;
58 /**< list of per-file meta-data information */
59 LIST_ENTRY(veriexec_dev_list) entries;
60 /**< next entries in the device list */
61 };
62
63 typedef LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_devhead_t;
64
65 /**
66 * @brief Mutex to protect the meta-data store lists
67 */
68 struct mtx ve_mutex;
69
70 /**
71 * @brief Executables meta-data storage
72 *
73 * This is used to store the fingerprints for potentially-executable files.
74 */
75 veriexec_devhead_t veriexec_dev_head;
76
77 /**
78 * @brief Plain file meta-data storage
79 *
80 * This is used for files that are not allowed to be executed, but should
81 * have fingerprint validation available.
82 */
83 veriexec_devhead_t veriexec_file_dev_head;
84
85 /**
86 * @internal
87 * @brief Search the @p head meta-data list for the specified file identifier
88 * @p fileid in the file system identified by @p fsid
89 *
90 * If meta-data exists for file system identified by @p fsid, it has a
91 * fingerprint list, and @p found_dev is not @c NULL then store true in the
92 * location pointed to by @p found_dev
93 *
94 * @param head meta-data list to search
95 * @param fsid file system identifier to look for
96 * @param fileid file to look for
97 * @param gen generation of file
98 * @param found_dev indicator that an entry for the file system was found
99 *
100 * @return A pointer to the meta-data inforation if meta-data exists for
101 * the specified file identifier, otherwise @c NULL
102 */
103 static struct mac_veriexec_file_info *
get_veriexec_file(struct veriexec_devhead * head,dev_t fsid,long fileid,unsigned long gen,int * found_dev)104 get_veriexec_file(struct veriexec_devhead *head, dev_t fsid, long fileid,
105 unsigned long gen, int *found_dev)
106 {
107 struct veriexec_dev_list *lp;
108 struct mac_veriexec_file_info *ip, *tip;
109
110 ip = NULL;
111
112 /* Initialize the value found_dev, if non-NULL */
113 if (found_dev != NULL)
114 *found_dev = 0;
115
116 VERIEXEC_DEBUG(3, ("searching for file %ju.%lu on device %ju,"
117 " files=%d\n", (uintmax_t)fileid, gen, (uintmax_t)fsid,
118 (head == &veriexec_file_dev_head)));
119
120 /* Get a lock to access the list */
121 mtx_lock(&ve_mutex);
122
123 /* First, look for the file system */
124 for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries))
125 if (lp->fsid == fsid)
126 break;
127
128 /* We found the file system in the list */
129 if (lp != NULL) {
130 VERIEXEC_DEBUG(3, ("found matching dev number %ju\n",
131 (uintmax_t)lp->fsid));
132
133 /* If found_dev is non-NULL, store true there */
134 if (found_dev != NULL)
135 *found_dev = 1;
136
137 /* Next, look for the meta-data information for the file */
138 LIST_FOREACH_SAFE(ip, &(lp->file_head), entries, tip) {
139 if (ip->fileid == fileid) {
140 if (ip->gen == gen)
141 break;
142 /* we need to garbage collect */
143 LIST_REMOVE(ip, entries);
144 if (ip->label)
145 free(ip->label, M_VERIEXEC);
146 free(ip, M_VERIEXEC);
147 }
148 }
149 }
150
151 /* Release the lock we obtained earlier */
152 mtx_unlock(&ve_mutex);
153
154 /* Return the meta-data information we found, if anything */
155 return (ip);
156 }
157
158 /**
159 * @internal
160 * @brief Display the fingerprint for each entry in the device list
161 *
162 * @param sbp sbuf to write output to
163 * @param lp pointer to device list
164 */
165 static void
mac_veriexec_print_db_dev_list(struct sbuf * sbp,struct veriexec_dev_list * lp)166 mac_veriexec_print_db_dev_list(struct sbuf *sbp, struct veriexec_dev_list *lp)
167 {
168 struct mac_veriexec_file_info *ip;
169
170 #define FPB(i) (ip->fingerprint[i])
171 for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
172 ip = LIST_NEXT(ip, entries))
173 sbuf_printf(sbp, " %ld: %u %ld [%02x %02x %02x %02x %02x "
174 "%02x %02x %02x...]\n", ip->fileid, ip->flags, ip->gen,
175 FPB(0), FPB(1), FPB(2), FPB(3), FPB(4), FPB(5), FPB(6),
176 FPB(7));
177 }
178
179 /**
180 * @internal
181 * @brief Display the device list
182 *
183 * @param sbp sbuf to write output to
184 * @param head pointer to head of the device list
185 */
186 static void
mac_veriexec_print_db_head(struct sbuf * sbp,struct veriexec_devhead * head)187 mac_veriexec_print_db_head(struct sbuf *sbp, struct veriexec_devhead *head)
188 {
189 struct veriexec_dev_list *lp;
190
191 for (lp = LIST_FIRST(head); lp != NULL; lp = LIST_NEXT(lp, entries)) {
192 sbuf_printf(sbp, " FS id: %ju\n", (uintmax_t)lp->fsid);
193 mac_veriexec_print_db_dev_list(sbp, lp);
194 }
195
196 }
197
198 /**
199 * @internal
200 * @brief Generate human-readable output for the current fingerprint database
201 *
202 * @param sbp sbuf to write output to
203 */
204 void
mac_veriexec_metadata_print_db(struct sbuf * sbp)205 mac_veriexec_metadata_print_db(struct sbuf *sbp)
206 {
207 struct {
208 struct veriexec_devhead *h;
209 const char *name;
210 } fpdbs[] = {
211 { &veriexec_file_dev_head, "regular files" },
212 { &veriexec_dev_head, "executable files" },
213 };
214 int i;
215
216 mtx_lock(&ve_mutex);
217 for (i = 0; i < sizeof(fpdbs)/sizeof(fpdbs[0]); i++) {
218 sbuf_printf(sbp, "%s fingerprint db:\n", fpdbs[i].name);
219 mac_veriexec_print_db_head(sbp, fpdbs[i].h);
220 }
221 mtx_unlock(&ve_mutex);
222 }
223 /**
224 * @brief Determine if the meta-data store has an entry for the specified file.
225 *
226 * @param fsid file system identifier to look for
227 * @param fileid file to look for
228 * @param gen generation of file
229 *
230 * @return 1 if there is an entry in the meta-data store, 0 otherwise.
231 */
232 int
mac_veriexec_metadata_has_file(dev_t fsid,long fileid,unsigned long gen)233 mac_veriexec_metadata_has_file(dev_t fsid, long fileid, unsigned long gen)
234 {
235
236 return (mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
237 NULL, VERIEXEC_FILES_FIRST) == 0);
238 }
239
240 /**
241 * @brief Search the list of devices looking for the one given, in order to
242 * release the resources used by it.
243 *
244 * If found, free all file entries for it, and remove it from the list.
245 *
246 * @note Called with @a ve_mutex held
247 *
248 * @param fsid file system identifier to look for
249 * @param head meta-data list to search
250 *
251 * @return 0 if the device entry was freed, otherwise an error code
252 */
253 static int
free_veriexec_dev(dev_t fsid,struct veriexec_devhead * head)254 free_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
255 {
256 struct veriexec_dev_list *lp;
257 struct mac_veriexec_file_info *ip, *nip;
258
259 /* Look for the file system */
260 for (lp = LIST_FIRST(head); lp != NULL;
261 lp = LIST_NEXT(lp, entries))
262 if (lp->fsid == fsid) break;
263
264 /* If lp is NULL, we did not find it */
265 if (lp == NULL)
266 return ENOENT;
267
268 /* Unhook lp, before we free it and its content */
269 LIST_REMOVE(lp, entries);
270
271 /* Release the lock */
272 mtx_unlock(&ve_mutex);
273
274 /* Free the file entries in the list */
275 for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL; ip = nip) {
276 nip = LIST_NEXT(ip, entries);
277 LIST_REMOVE(ip, entries);
278 if (ip->label)
279 free(ip->label, M_VERIEXEC);
280 free(ip, M_VERIEXEC);
281 }
282
283 /* Free the meta-data entry for the device */
284 free(lp, M_VERIEXEC);
285
286 /* Re-acquire the lock */
287 mtx_lock(&ve_mutex);
288 return 0;
289 }
290
291 /**
292 * @brief Search the list of devices looking for the one given.
293 *
294 * If it is not in the list then add it.
295 *
296 * @note Called with @a ve_mutex held
297 *
298 * @param fsid file system identifier to look for
299 * @param head meta-data list to search
300 *
301 * @return A pointer to the meta-data entry for the device, if found or added,
302 * otherwise @c NULL
303 */
304 static struct veriexec_dev_list *
find_veriexec_dev(dev_t fsid,struct veriexec_devhead * head)305 find_veriexec_dev(dev_t fsid, struct veriexec_devhead *head)
306 {
307 struct veriexec_dev_list *lp;
308 struct veriexec_dev_list *np = NULL;
309
310 search:
311 /* Look for the file system */
312 for (lp = LIST_FIRST(head); lp != NULL;
313 lp = LIST_NEXT(lp, entries))
314 if (lp->fsid == fsid) break;
315
316 if (lp == NULL) {
317 if (np == NULL) {
318 /*
319 * If pointer is null then entry not there,
320 * add a new one, first try to malloc while
321 * we hold mutex - should work most of the time.
322 */
323 np = malloc(sizeof(struct veriexec_dev_list),
324 M_VERIEXEC, M_NOWAIT);
325 if (np == NULL) {
326 /*
327 * So much for that plan, dop the mutex
328 * and repeat...
329 */
330 mtx_unlock(&ve_mutex);
331 np = malloc(sizeof(struct veriexec_dev_list),
332 M_VERIEXEC, M_WAITOK);
333 mtx_lock(&ve_mutex);
334 /*
335 * Repeat the search, in case someone
336 * added this while we slept.
337 */
338 goto search;
339 }
340 }
341 if (np) {
342 /* Add the entry to the list */
343 lp = np;
344 LIST_INIT(&(lp->file_head));
345 lp->fsid = fsid;
346 LIST_INSERT_HEAD(head, lp, entries);
347 }
348 } else if (np) {
349 /*
350 * Someone else did it while we slept.
351 */
352 mtx_unlock(&ve_mutex);
353 free(np, M_VERIEXEC);
354 mtx_lock(&ve_mutex);
355 }
356
357 return (lp);
358 }
359
360 /**
361 * @internal
362 * @brief Allocate and initialize label record with the provided data.
363 *
364 * @param labelp Location to store the initialized label
365 * @param src Pointer to label string to copy
366 * @param srclen Length of label string to copy
367 *
368 * @return Length of resulting label
369 *
370 * @note Called with ve_mutex locked.
371 */
372 static size_t
mac_veriexec_init_label(char ** labelp,size_t labellen,char * src,size_t srclen)373 mac_veriexec_init_label(char **labelp, size_t labellen, char *src,
374 size_t srclen)
375 {
376 char *label;
377
378 label = *labelp;
379 if (labellen < srclen) {
380 mtx_unlock(&ve_mutex);
381 if (label != NULL)
382 free(label, M_VERIEXEC);
383 label = malloc(srclen, M_VERIEXEC, M_WAITOK);
384 mtx_lock(&ve_mutex);
385 labellen = srclen;
386 *labelp = label;
387 }
388 memcpy(label, src, srclen);
389 return labellen;
390 }
391
392 /**
393 * @brief When a device is unmounted, we want to toss the signatures recorded
394 * against it.
395 *
396 * We are being called from unmount() with the root vnode just before it is
397 * freed.
398 *
399 * @param fsid file system identifier to look for
400 * @param td calling thread
401 *
402 * @return 0 on success, otherwise an error code.
403 */
404 int
mac_veriexec_metadata_unmounted(dev_t fsid,struct thread * td)405 mac_veriexec_metadata_unmounted(dev_t fsid, struct thread *td)
406 {
407 int error;
408
409 /*
410 * The device can have entries on both lists.
411 */
412 mtx_lock(&ve_mutex);
413 error = free_veriexec_dev(fsid, &veriexec_dev_head);
414 if (error && error != ENOENT) {
415 mtx_unlock(&ve_mutex);
416 return error;
417 }
418 error = free_veriexec_dev(fsid, &veriexec_file_dev_head);
419 mtx_unlock(&ve_mutex);
420 if (error && error != ENOENT) {
421 return error;
422 }
423 return 0;
424 }
425
426 /**
427 * @brief Return the flags assigned to the file identified by file system
428 * identifier @p fsid and file identifier @p fileid.
429 *
430 * @param fsid file system identifier
431 * @param fileid file identifier within the file system
432 * @param gen generation of file
433 * @param flags pointer to location to store the flags
434 * @param check_files if 1, check the files list first, otherwise check the
435 * exectuables list first
436 *
437 * @return 0 on success, otherwise an error code.
438 */
439 int
mac_veriexec_metadata_get_file_flags(dev_t fsid,long fileid,unsigned long gen,int * flags,int check_files)440 mac_veriexec_metadata_get_file_flags(dev_t fsid, long fileid, unsigned long gen,
441 int *flags, int check_files)
442 {
443 struct mac_veriexec_file_info *ip;
444 int error;
445
446 error = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
447 &ip, check_files);
448 if (error != 0)
449 return (error);
450
451 *flags = ip->flags;
452 return (0);
453 }
454
455 /**
456 * @brief get the files for the specified process
457 *
458 * @param cred credentials to use
459 * @param p process to get the flags for
460 * @param flags where to store the flags
461 * @param check_files if 1, check the files list first, otherwise check the
462 * exectuables list first
463 *
464 * @return 0 if the process has an entry in the meta-data store, otherwise an
465 * error code
466 */
467 int
mac_veriexec_metadata_get_executable_flags(struct ucred * cred,struct proc * p,int * flags,int check_files)468 mac_veriexec_metadata_get_executable_flags(struct ucred *cred, struct proc *p,
469 int *flags, int check_files)
470 {
471 struct vnode *proc_vn;
472 struct vattr vap;
473 int error;
474
475 /* Get the text vnode for the process */
476 proc_vn = p->p_textvp;
477 if (proc_vn == NULL)
478 return EINVAL;
479
480 /* Get vnode attributes */
481 error = VOP_GETATTR(proc_vn, &vap, cred);
482 if (error)
483 return error;
484
485 error = mac_veriexec_metadata_get_file_flags(vap.va_fsid,
486 vap.va_fileid, vap.va_gen, flags,
487 (check_files == VERIEXEC_FILES_FIRST));
488
489 return (error);
490 }
491
492 /**
493 * @brief Ensure the fingerprint status for the vnode @p vp is assigned to its
494 * MAC label.
495 *
496 * @param vp vnode to check
497 * @param vap vnode attributes to use
498 * @param td calling thread
499 * @param check_files if 1, check the files list first, otherwise check the
500 * exectuables list first
501 *
502 * @return 0 on success, otherwise an error code.
503 */
504 int
mac_veriexec_metadata_fetch_fingerprint_status(struct vnode * vp,struct vattr * vap,struct thread * td,int check_files)505 mac_veriexec_metadata_fetch_fingerprint_status(struct vnode *vp,
506 struct vattr *vap, struct thread *td, int check_files)
507 {
508 unsigned char digest[MAXFINGERPRINTLEN];
509 struct mac_veriexec_file_info *ip;
510 int error, found_dev;
511 fingerprint_status_t status;
512
513 error = 0;
514 ip = NULL;
515
516 status = mac_veriexec_get_fingerprint_status(vp);
517 if (status == FINGERPRINT_INVALID || status == FINGERPRINT_NODEV) {
518 found_dev = 0;
519 if (mac_veriexec_metadata_get_file_info(vap->va_fsid,
520 vap->va_fileid, vap->va_gen, &found_dev, &ip,
521 check_files) != 0) {
522 status = (found_dev) ? FINGERPRINT_NOENTRY :
523 FINGERPRINT_NODEV;
524 VERIEXEC_DEBUG(3,
525 ("fingerprint status is %d for dev %ju, file "
526 "%ju.%lu\n", status, (uintmax_t)vap->va_fsid,
527 (uintmax_t)vap->va_fileid, vap->va_gen));
528 } else {
529 /*
530 * evaluate and compare fingerprint
531 */
532 error = mac_veriexec_fingerprint_check_vnode(vp, ip,
533 td, vap->va_size, digest);
534 switch (error) {
535 case 0:
536 /* Process flags */
537 if ((ip->flags & VERIEXEC_INDIRECT))
538 status = FINGERPRINT_INDIRECT;
539 else if ((ip->flags & VERIEXEC_FILE))
540 status = FINGERPRINT_FILE;
541 else
542 status = FINGERPRINT_VALID;
543 VERIEXEC_DEBUG(2,
544 ("%sfingerprint matches for dev %ju, file "
545 "%ju.%lu\n",
546 (status == FINGERPRINT_INDIRECT) ?
547 "indirect " :
548 (status == FINGERPRINT_FILE) ?
549 "file " : "", (uintmax_t)vap->va_fsid,
550 (uintmax_t)vap->va_fileid, vap->va_gen));
551 break;
552
553 case EAUTH:
554 #ifdef MAC_VERIEXEC_DEBUG
555 {
556 char have[MAXFINGERPRINTLEN * 2 + 1];
557 char want[MAXFINGERPRINTLEN * 2 + 1];
558 int i, len;
559
560 len = ip->ops->digest_len;
561 for (i = 0; i < len; i++) {
562 sprintf(&want[i * 2], "%02x",
563 ip->fingerprint[i]);
564 sprintf(&have[i * 2], "%02x",
565 digest[i]);
566 }
567 log(LOG_ERR, MAC_VERIEXEC_FULLNAME
568 ": fingerprint for dev %ju, file "
569 "%ju.%lu %s != %s\n",
570 (uintmax_t)vap->va_fsid,
571 (uintmax_t)vap->va_fileid,
572 vap->va_gen,
573 have, want);
574 }
575 #endif
576 status = FINGERPRINT_NOMATCH;
577 break;
578 default:
579 VERIEXEC_DEBUG(2,
580 ("fingerprint status error %d\n", error));
581 break;
582 }
583 }
584 mac_veriexec_set_fingerprint_status(vp, status);
585 }
586 return (error);
587 }
588
589 /**
590 * Return label if we have one
591 *
592 * @param fsid file system identifier to look for
593 * @param fileid file to look for
594 * @param gen generation of file
595 * @param check_files look at non-executable files?
596 *
597 * @return A pointer to the label or @c NULL
598 */
599 const char *
mac_veriexec_metadata_get_file_label(dev_t fsid,long fileid,unsigned long gen,int check_files)600 mac_veriexec_metadata_get_file_label(dev_t fsid, long fileid,
601 unsigned long gen, int check_files)
602 {
603 struct mac_veriexec_file_info *ip;
604 int error;
605
606 error = mac_veriexec_metadata_get_file_info(fsid, fileid, gen, NULL,
607 &ip, check_files);
608 if (error)
609 return (NULL);
610
611 return ((ip->flags & VERIEXEC_LABEL) != 0 ? ip->label : NULL);
612 }
613
614 /**
615 * Add a file and its fingerprint to the list of files attached
616 * to the device @p fsid.
617 *
618 * Only add the entry if it is not already on the list.
619 *
620 * @note Called with @a ve_mutex held
621 *
622 * @param file_dev if 1, the entry should be added on the file list,
623 * otherwise it should be added on the executable list
624 * @param fsid file system identifier of device
625 * @param fileid file to add
626 * @param gen generation of file
627 * @param fingerprint fingerprint to add to the store
628 * @param flags flags to set in the store
629 * @param fp_type digest type
630 * @param override if 1, override any values already stored
631 *
632 * @return 0 on success, otherwise an error code.
633 */
634 int
mac_veriexec_metadata_add_file(int file_dev,dev_t fsid,long fileid,unsigned long gen,unsigned char fingerprint[MAXFINGERPRINTLEN],char * label,size_t labellen,int flags,const char * fp_type,int override)635 mac_veriexec_metadata_add_file(int file_dev, dev_t fsid, long fileid,
636 unsigned long gen, unsigned char fingerprint[MAXFINGERPRINTLEN],
637 char *label, size_t labellen, int flags, const char *fp_type, int override)
638 {
639 struct mac_veriexec_fpops *fpops;
640 struct veriexec_dev_list *lp;
641 struct veriexec_devhead *head;
642 struct mac_veriexec_file_info *ip;
643 struct mac_veriexec_file_info *np = NULL;
644
645 /* Label and labellen must be set if VERIEXEC_LABEL is set */
646 if ((flags & VERIEXEC_LABEL) != 0 && (label == NULL || labellen == 0))
647 return (EINVAL);
648
649 /* Look up the device entry */
650 if (file_dev)
651 head = &veriexec_file_dev_head;
652 else
653 head = &veriexec_dev_head;
654 lp = find_veriexec_dev(fsid, head);
655
656 /* Look up the fingerprint operations for the digest type */
657 fpops = mac_veriexec_fingerprint_lookup_ops(fp_type);
658 if (fpops == NULL)
659 return (EOPNOTSUPP);
660
661 search:
662 for (ip = LIST_FIRST(&(lp->file_head)); ip != NULL;
663 ip = LIST_NEXT(ip, entries)) {
664 /* check for a dupe file in the list, skip if an entry
665 * exists for this file except for when the flags contains
666 * VERIEXEC_INDIRECT, always set the flags when it is so
667 * we don't get a hole caused by conflicting flags on
668 * hardlinked files. XXX maybe we should validate
669 * fingerprint is same and complain if it is not...
670 */
671 if (ip->fileid == fileid && ip->gen == gen) {
672 if (override) {
673 /*
674 * for a signed load we allow overrides,
675 * otherwise fingerpints needed for pkg loads
676 * can fail (the files are on temp device).
677 */
678 ip->flags = flags;
679 ip->ops = fpops;
680 memcpy(ip->fingerprint, fingerprint,
681 fpops->digest_len);
682 if (flags & VERIEXEC_LABEL) {
683 ip->labellen = mac_veriexec_init_label(
684 &ip->label, ip->labellen, label,
685 labellen);
686 } else if (ip->labellen > 0) {
687 free(ip->label, M_VERIEXEC);
688 ip->labellen = 0;
689 ip->label = NULL;
690 }
691 } else if ((flags & (VERIEXEC_INDIRECT|VERIEXEC_FILE)))
692 ip->flags |= flags;
693
694 if (np) {
695 /* unlikely but... we don't need it now. */
696 mtx_unlock(&ve_mutex);
697 free(np, M_VERIEXEC);
698 mtx_lock(&ve_mutex);
699 }
700 return (0);
701 }
702 }
703
704 /*
705 * We may have been past here before...
706 */
707 if (np == NULL) {
708 /*
709 * We first try with mutex held and nowait.
710 */
711 np = malloc(sizeof(struct mac_veriexec_file_info), M_VERIEXEC,
712 M_NOWAIT);
713 if (np == NULL) {
714 /*
715 * It was worth a try, now
716 * drop mutex while we malloc.
717 */
718 mtx_unlock(&ve_mutex);
719 np = malloc(sizeof(struct mac_veriexec_file_info),
720 M_VERIEXEC, M_WAITOK);
721 mtx_lock(&ve_mutex);
722 /*
723 * We now have to repeat our search!
724 */
725 goto search;
726 }
727 }
728
729 /* Set up the meta-data entry */
730 ip = np;
731 ip->flags = flags;
732 ip->ops = fpops;
733 ip->fileid = fileid;
734 ip->gen = gen;
735 ip->label = NULL;
736 ip->labellen = 0;
737 memcpy(ip->fingerprint, fingerprint, fpops->digest_len);
738 if (flags & VERIEXEC_LABEL)
739 ip->labellen = mac_veriexec_init_label(&ip->label,
740 ip->labellen, label, labellen);
741
742 VERIEXEC_DEBUG(3, ("add file %ju.%lu (files=%d)\n",
743 (uintmax_t)ip->fileid,
744 ip->gen, file_dev));
745
746 /* Add the entry to the list */
747 LIST_INSERT_HEAD(&(lp->file_head), ip, entries);
748 #ifdef DEBUG_VERIEXEC_FINGERPRINT
749 {
750 off_t offset;
751
752 printf("Stored %s fingerprint:\n", fp_type);
753 for (offset = 0; offset < fpops->digest_len; offset++)
754 printf("%02x", fingerprint[offset]);
755 printf("\n");
756 }
757 #endif
758 return (0);
759 }
760
761 /**
762 * @brief Search the meta-data store for information on the specified file.
763 *
764 * @param fsid file system identifier to look for
765 * @param fileid file to look for
766 * @param gen generation of file
767 * @param found_dev indicator that an entry for the file system was found
768 * @param ipp pointer to location to store the info pointer
769 * @param check_files if 1, check the files list first, otherwise check the
770 * exectuables list first
771 *
772 * @return A pointer to the meta-data inforation if meta-data exists for
773 * the specified file identifier, otherwise @c NULL
774 */
775 int
mac_veriexec_metadata_get_file_info(dev_t fsid,long fileid,unsigned long gen,int * found_dev,struct mac_veriexec_file_info ** ipp,int check_files)776 mac_veriexec_metadata_get_file_info(dev_t fsid, long fileid, unsigned long gen,
777 int *found_dev, struct mac_veriexec_file_info **ipp, int check_files)
778 {
779 struct veriexec_devhead *search[3];
780 struct mac_veriexec_file_info *ip;
781 int x;
782
783 /* Determine the order of the lists to search */
784 if (check_files) {
785 search[0] = &veriexec_file_dev_head;
786 search[1] = &veriexec_dev_head;
787 } else {
788 search[0] = &veriexec_dev_head;
789 search[1] = &veriexec_file_dev_head;
790 }
791 search[2] = NULL;
792
793 VERIEXEC_DEBUG(3, ("%s: searching for dev %#jx, file %lu.%lu\n",
794 __func__, (uintmax_t)fsid, fileid, gen));
795
796 /* Search for the specified file */
797 for (ip = NULL, x = 0; ip == NULL && search[x]; x++)
798 ip = get_veriexec_file(search[x], fsid, fileid, gen, found_dev);
799
800 if (ipp != NULL)
801 *ipp = ip;
802 if (ip == NULL)
803 return (ENOENT);
804 return (0);
805 }
806
807 /**
808 * @brief Intialize the meta-data store
809 */
810 void
mac_veriexec_metadata_init(void)811 mac_veriexec_metadata_init(void)
812 {
813
814 mtx_init(&ve_mutex, "veriexec lock", NULL, MTX_DEF);
815 LIST_INIT(&veriexec_dev_head);
816 LIST_INIT(&veriexec_file_dev_head);
817 }
818