Index: head/lib/libsecureboot/Makefile.libsa.inc =================================================================== --- head/lib/libsecureboot/Makefile.libsa.inc +++ head/lib/libsecureboot/Makefile.libsa.inc @@ -29,6 +29,11 @@ -I${SRCTOP}/stand/efi/include/${MACHINE} .endif +.if ${MK_LOADER_VERIEXEC_PASS_MANIFEST} == "yes" +SRCS+= \ + pass_manifest.c +.endif + # this is the list of paths (relative to a file # that we need to verify) used to find a signed manifest. # the signature extensions in VE_SIGNATURE_EXT_LIST Index: head/lib/libsecureboot/h/verify_file.h =================================================================== --- head/lib/libsecureboot/h/verify_file.h +++ head/lib/libsecureboot/h/verify_file.h @@ -32,6 +32,7 @@ #define VE_WANT 1 /* we want this verified */ #define VE_MUST 2 /* this must be verified */ +#define VE_NOT_CHECKED -42 #define VE_VERIFIED 1 /* all good */ #define VE_UNVERIFIED_OK 0 /* not verified but that's ok */ #define VE_NOT_VERIFYING 2 /* we are not verifying */ @@ -42,6 +43,8 @@ int ve_status_get(int); void ve_efi_init(void); int load_manifest(const char *, const char *, const char *, struct stat *); +int pass_manifest(const char *, const char *); +int pass_manifest_export_envs(void); int verify_file(int, const char *, off_t, int); void verify_pcr_export(void); Index: head/lib/libsecureboot/libsecureboot-priv.h =================================================================== --- head/lib/libsecureboot/libsecureboot-priv.h +++ head/lib/libsecureboot/libsecureboot-priv.h @@ -31,6 +31,8 @@ /* public api */ #include "libsecureboot.h" +struct stat; + typedef struct { unsigned char *data; size_t hash_size; @@ -49,6 +51,9 @@ const unsigned char *hash_oid, unsigned char *mdata, size_t mlen, unsigned char *sdata, size_t slen); + +int is_verified(struct stat *stp); +void add_verify_status(struct stat *stp, int status); int openpgp_self_tests(void); Index: head/lib/libsecureboot/pass_manifest.c =================================================================== --- head/lib/libsecureboot/pass_manifest.c +++ head/lib/libsecureboot/pass_manifest.c @@ -0,0 +1,152 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include +__FBSDID("$FreeBSD$"); + +#include + +#include "libsecureboot-priv.h" +#include + +/* + * Values to pass to kernel by envs. + */ +static char manifest_path[MAXPATHLEN]; +static char manifest_prefix[MAXPATHLEN]; +static char manifest_hash[2 * br_sha256_SIZE + 2]; +static int manifest_present = 0; + +/* + * Verify and pass manifest path and digest to kernel through envs. + * The paths in manifest can be either absolute, + * or "prefix", if exists will be added to the ones that are not. + */ +int +pass_manifest(const char *path, const char *prefix) +{ + char *content; + struct stat st; + unsigned char digest[br_sha256_SIZE]; + const br_hash_class *md; + br_hash_compat_context ctx; + int rc; + + content = NULL; + md = &br_sha256_vtable; + + if (strnlen(path, MAXPATHLEN) == MAXPATHLEN || + strnlen(prefix, MAXPATHLEN) == MAXPATHLEN) + return (EINVAL); + + rc = stat(path, &st); + if (rc != 0) + goto out; + + if (!S_ISREG(st.st_mode)) { + rc = EINVAL; + goto out; + } + + rc = is_verified(&st); + + if (rc != VE_NOT_CHECKED && rc != VE_VERIFIED) { + rc = EPERM; + goto out; + } + + if (rc == VE_VERIFIED) + content = read_file(path, NULL); + else + content = (char *)verify_signed(path, VEF_VERBOSE); + + if (content == NULL) { + add_verify_status(&st, VE_FINGERPRINT_WRONG); + rc = EIO; + goto out; + } + + add_verify_status(&st, VE_VERIFIED); + + md->init(&ctx.vtable); + md->update(&ctx.vtable, content, st.st_size); + md->out(&ctx.vtable, digest); + + if (prefix == NULL) + manifest_prefix[0] = '\0'; + else + strcpy(manifest_prefix, prefix); + + strcpy(manifest_path, path); + + hexdigest(manifest_hash, 2 * br_sha256_SIZE + 2, + digest, br_sha256_SIZE); + manifest_hash[2*br_sha256_SIZE] = '\0'; + + manifest_present = 1; + rc = 0; + +out: + if (content != NULL) + free(content); + + return (rc); +} + +/* + * Set appropriate envs to inform kernel about manifest location and digest. + * This should be called right before boot so that envs can't be replaced. + */ +int +pass_manifest_export_envs() +{ + int rc; + + /* If we have nothing to pass make sure that envs are empty. */ + if (!manifest_present) { + unsetenv("veriexec.manifest_path"); + unsetenv("veriexec.manifest_hash"); + unsetenv("veriexec.manifest_prefix"); + return (0); + } + + rc = setenv("veriexec.manifest_path", manifest_path, 1); + if (rc != 0) + return (rc); + + rc = setenv("veriexec.manifest_hash", manifest_hash, 1); + if (rc != 0) { + unsetenv("veriexec.manifest_path"); + return (rc); + } + + if (manifest_prefix[0] != '\0') + rc = setenv("veriexec.manifest_prefix", manifest_prefix, 1); + + return (rc); +} + Index: head/lib/libsecureboot/verify_file.c =================================================================== --- head/lib/libsecureboot/verify_file.c +++ head/lib/libsecureboot/verify_file.c @@ -36,8 +36,6 @@ #include #include -#define VE_NOT_CHECKED -42 - #ifdef UNIT_TEST # include # define panic warn @@ -112,7 +110,7 @@ struct verify_status *vs_next; }; -static int +int is_verified(struct stat *stp) { struct verify_status *vsp; @@ -126,7 +124,7 @@ } /* most recent first, since most likely to see repeated calls. */ -static void +void add_verify_status(struct stat *stp, int status) { struct verify_status *vsp; Index: head/share/mk/src.opts.mk =================================================================== --- head/share/mk/src.opts.mk +++ head/share/mk/src.opts.mk @@ -205,6 +205,7 @@ LOADER_FIREWIRE \ LOADER_FORCE_LE \ LOADER_VERBOSE \ + LOADER_VERIEXEC_PASS_MANIFEST \ NAND \ OFED_EXTRA \ OPENLDAP \ @@ -545,6 +546,10 @@ MK_CLANG_EXTRAS:= no MK_CLANG_FULL:= no MK_LLVM_COV:= no +.endif + +.if ${MK_LOADER_VERIEXEC} == "no" +MK_LOADER_VERIEXEC_PASS_MANIFEST := no .endif # Index: head/stand/common/boot.c =================================================================== --- head/stand/common/boot.c +++ head/stand/common/boot.c @@ -108,6 +108,9 @@ #ifdef LOADER_VERIEXEC verify_pcr_export(); /* for measured boot */ +#ifdef LOADER_VERIEXEC_PASS_MANIFEST + pass_manifest_export_envs(); +#endif #endif /* Call the exec handler from the loader matching the kernel */ Index: head/stand/common/module.c =================================================================== --- head/stand/common/module.c +++ head/stand/common/module.c @@ -159,6 +159,13 @@ ve_debug_set(dflag); return (load_manifest(argv[1], prefix, skip, NULL)); } +#ifdef LOADER_VERIEXEC_PASS_MANIFEST + if (strncmp(typestr, "pass_manifest", 13) == 0) { + if (dflag > 0) + ve_debug_set(dflag); + return (pass_manifest(argv[1], prefix)); + } +#endif #endif fp = file_findfile(argv[1], typestr); Index: head/stand/loader.mk =================================================================== --- head/stand/loader.mk +++ head/stand/loader.mk @@ -77,6 +77,10 @@ CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h .endif +.if ${MK_LOADER_VERIEXEC_PASS_MANIFEST} != "no" +CFLAGS+= -DLOADER_VERIEXEC_PASS_MANIFEST -I${SRCTOP}/lib/libsecureboot/h +.endif + .if defined(BOOT_PROMPT_123) CFLAGS+= -DBOOT_PROMPT_123 .endif Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -4890,14 +4890,15 @@ security/mac_seeotheruids/mac_seeotheruids.c optional mac_seeotheruids security/mac_stub/mac_stub.c optional mac_stub security/mac_test/mac_test.c optional mac_test -security/mac_veriexec/mac_veriexec.c optional mac_veriexec -security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec -security/mac_veriexec/veriexec_metadata.c optional mac_veriexec -security/mac_veriexec/mac_veriexec_rmd160.c optional mac_veriexec_rmd160 -security/mac_veriexec/mac_veriexec_sha1.c optional mac_veriexec_sha1 -security/mac_veriexec/mac_veriexec_sha256.c optional mac_veriexec_sha256 -security/mac_veriexec/mac_veriexec_sha384.c optional mac_veriexec_sha384 -security/mac_veriexec/mac_veriexec_sha512.c optional mac_veriexec_sha512 +security/mac_veriexec/mac_veriexec.c optional mac_veriexec +security/mac_veriexec/veriexec_fingerprint.c optional mac_veriexec +security/mac_veriexec/veriexec_metadata.c optional mac_veriexec +security/mac_veriexec_parser/mac_veriexec_parser.c optional mac_veriexec mac_veriexec_parser +security/mac_veriexec/mac_veriexec_rmd160.c optional mac_veriexec_rmd160 +security/mac_veriexec/mac_veriexec_sha1.c optional mac_veriexec_sha1 +security/mac_veriexec/mac_veriexec_sha256.c optional mac_veriexec_sha256 +security/mac_veriexec/mac_veriexec_sha384.c optional mac_veriexec_sha384 +security/mac_veriexec/mac_veriexec_sha512.c optional mac_veriexec_sha512 teken/teken.c optional sc !SC_NO_TERM_TEKEN | vt ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_balloc.c optional ffs Index: head/sys/security/mac_veriexec_parser/mac_veriexec_parser.c =================================================================== --- head/sys/security/mac_veriexec_parser/mac_veriexec_parser.c +++ head/sys/security/mac_veriexec_parser/mac_veriexec_parser.c @@ -0,0 +1,474 @@ +/*- + * Copyright (c) 2019 Stormshield. + * Copyright (c) 2019 Semihalf. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* The following are based on sbin/veriexec */ +struct fingerprint_type { + const char *fp_type; + int fp_size; +}; + +struct fp_flag { + const char *flag_name; + int flag; +}; + +static const struct fingerprint_type fp_table[] = { + {"sha256=", SHA256_DIGEST_LENGTH}, +#if MAXFINGERPRINTLEN >= SHA384_DIGEST_LENGTH + {"sha384=", SHA384_DIGEST_LENGTH}, +#endif +#if MAXFINGERPRINTLEN >= SHA512_DIGEST_LENGTH + {"sha512=", SHA512_DIGEST_LENGTH}, +#endif + {NULL, 0} +}; + +static const struct fp_flag flags_table[] = { + {"indirect", VERIEXEC_INDIRECT}, + {"no_ptrace", VERIEXEC_NOTRACE}, + {"trusted", VERIEXEC_TRUSTED}, + {"no_fips", VERIEXEC_NOFIPS}, + {NULL, 0} +}; + +extern struct mtx ve_mutex; + +static unsigned char hexchar_to_byte(unsigned char c); +static int hexstring_to_bin(unsigned char *buf); + +static int get_flags(const char *entry); +static int get_fp(const char *entry, char **type, + unsigned char **digest, int *flags); +static int verify_digest(const char *data, size_t len, + const unsigned char *expected_hash); + +static int open_file(const char *path, struct nameidata *nid); +static char *read_manifest(char *path, unsigned char *digest); +static int parse_entry(char *entry, char *prefix); +static int parse_manifest(char *path, unsigned char *hash, char *prefix); + +static unsigned char +hexchar_to_byte(unsigned char c) +{ + + if (isdigit(c)) + return (c - '0'); + + return (isupper(c) ? c - 'A' + 10 : c - 'a' + 10); +} + +static int +hexstring_to_bin(unsigned char *buf) +{ + size_t i, len; + unsigned char byte; + + len = strlen(buf); + for (i = 0; i < len / 2; i++) { + if (!isxdigit(buf[2 * i]) || !isxdigit(buf[2 * i + 1])) + return (EINVAL); + + byte = hexchar_to_byte(buf[2 * i]) << 4; + byte += hexchar_to_byte(buf[2 * i + 1]); + buf[i] = byte; + } + return (0); +} + +static int +get_flags(const char *entry) +{ + int i; + int result = 0; + + for (i = 0; flags_table[i].flag_name != NULL; i++) + if (strstr(entry, flags_table[i].flag_name) != NULL) + result |= flags_table[i].flag; + + return (result); +} + +/* + * Parse a single line of manifest looking for a digest and its type. + * We expect it to be in form of "path shaX=hash". + * The line will be split into path, hash type and hash value. + */ +static int +get_fp(const char *entry, char **type, unsigned char **digest, int *flags) +{ + char *delimiter; + char *local_digest; + char *fp_type; + char *prev_fp_type; + size_t min_len; + int i; + + delimiter = NULL; + fp_type = NULL; + prev_fp_type = NULL; + + for (i = 0; fp_table[i].fp_type != NULL; i++) { + fp_type = strstr(entry, fp_table[i].fp_type); + /* Look for the last "shaX=hash" in line */ + while (fp_type != NULL) { + prev_fp_type = fp_type; + fp_type++; + fp_type = strstr(fp_type, fp_table[i].fp_type); + } + fp_type = prev_fp_type; + if (fp_type != NULL) { + if (fp_type == entry || fp_type[-1] != ' ') + return (EINVAL); + + /* + * The entry should contain at least + * fp_type and digest in hexadecimal form. + */ + min_len = strlen(fp_table[i].fp_type) + + 2 * fp_table[i].fp_size; + + if (strnlen(fp_type, min_len) < min_len) + return (EINVAL); + + local_digest = &fp_type[strlen(fp_table[i].fp_type)]; + delimiter = &local_digest[2 * fp_table[i].fp_size]; + + /* + * Make sure that digest is followed by + * some kind of delimiter. + */ + if (*delimiter != '\n' && + *delimiter != '\0' && + *delimiter != ' ') + return (EINVAL); + + /* + * Does the entry contain flags we need to parse? + */ + if (*delimiter == ' ' && flags != NULL) + *flags = get_flags(delimiter); + + /* + * Split entry into three parts: + * path, fp_type and digest. + */ + local_digest[-1] = '\0'; + *delimiter = '\0'; + fp_type[-1] = '\0'; + break; + } + } + + if (fp_type == NULL) + return (EINVAL); + + if (type != NULL) + *type = fp_type; + + if (digest != NULL) + *digest = local_digest; + + return (0); +} + +/* + * Currently we verify manifest using sha256. + * In future another env with hash type could be introduced. + */ +static int +verify_digest(const char *data, size_t len, const unsigned char *expected_hash) +{ + SHA256_CTX ctx; + unsigned char hash[SHA256_DIGEST_LENGTH]; + + SHA256_Init(&ctx); + SHA256_Update(&ctx, data, len); + SHA256_Final(hash, &ctx); + + return (memcmp(expected_hash, hash, SHA256_DIGEST_LENGTH)); +} + + +static int +open_file(const char *path, struct nameidata *nid) +{ + int flags, rc; + + flags = FREAD; + + pwd_ensure_dirs(); + + NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path, curthread); + rc = vn_open(nid, &flags, 0, NULL); + NDFREE(nid, NDF_ONLY_PNBUF); + if (rc != 0) + return (rc); + + return (0); +} + +/* + * Read the manifest from location specified in path and verify its digest. + */ +static char* +read_manifest(char *path, unsigned char *digest) +{ + struct nameidata nid; + struct vattr va; + char *data; + ssize_t bytes_read, resid; + int rc; + + data = NULL; + bytes_read = 0; + + rc = open_file(path, &nid); + if (rc != 0) + goto fail; + + rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); + if (rc != 0) + goto fail; + + data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK); + + while (bytes_read < va.va_size) { + rc = vn_rdwr( + UIO_READ, nid.ni_vp, data, + va.va_size - bytes_read, bytes_read, + UIO_SYSSPACE, IO_NODELOCKED, + curthread->td_ucred, NOCRED, &resid, curthread); + if (rc != 0) + goto fail; + + bytes_read = va.va_size - resid; + } + + data[bytes_read] = '\0'; + + VOP_UNLOCK(nid.ni_vp, 0); + (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); + + /* + * If digest is wrong someone might be trying to fool us. + */ + if (verify_digest(data, va.va_size, digest)) + panic("Manifest hash doesn't match expected value!"); + + return (data); + +fail: + if (data != NULL) + free(data, M_VERIEXEC); + + return (NULL); +} + +/* + * Process single line. + * First split it into path, digest_type and digest. + * Then try to open the file and insert its fingerprint into metadata store. + */ +static int +parse_entry(char *entry, char *prefix) +{ + struct nameidata nid; + struct vattr va; + char path[MAXPATHLEN]; + char *fp_type; + unsigned char *digest; + int rc, is_exec, flags; + + fp_type = NULL; + digest = NULL; + flags = 0; + + rc = get_fp(entry, &fp_type, &digest, &flags); + if (rc != 0) + return (rc); + + rc = hexstring_to_bin(digest); + if (rc != 0) + return (rc); + + if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN) + return (EINVAL); + + /* If the path is not absolute prepend it with a prefix */ + if (prefix != NULL && entry[0] != '/') { + rc = snprintf(path, MAXPATHLEN, "%s/%s", + prefix, entry); + if (rc < 0) + return (-rc); + } else { + strcpy(path, entry); + } + + rc = open_file(path, &nid); + NDFREE(&nid, NDF_ONLY_PNBUF); + if (rc != 0) + return (rc); + + rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred); + if (rc != 0) + goto out; + + is_exec = (va.va_mode & VEXEC); + + mtx_lock(&ve_mutex); + rc = mac_veriexec_metadata_add_file( + is_exec == 0, + va.va_fsid, va.va_fileid, va.va_gen, + digest, flags, fp_type, 1); + mtx_unlock(&ve_mutex); + +out: + VOP_UNLOCK(nid.ni_vp, 0); + vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread); + return (rc); +} + +/* + * Look for manifest in env that have beed passed by loader. + * This routine should be called right after the rootfs is mounted. + */ +static int +parse_manifest(char *path, unsigned char *hash, char *prefix) +{ + char *data; + char *entry; + char *next_entry; + int rc, success_count; + + data = NULL; + success_count = 0; + rc = 0; + + data = read_manifest(path, hash); + if (data == NULL) { + rc = EIO; + goto out; + } + + entry = data; + while (entry != NULL) { + next_entry = strchr(entry, '\n'); + if (next_entry != NULL) { + *next_entry = '\0'; + next_entry++; + } + if (entry[0] == '\n' || entry[0] == '\0') { + entry = next_entry; + continue; + } + if ((rc = parse_entry(entry, prefix))) + printf("mac_veriexec_parser: Warning: Failed to parse" + " entry with rc:%d, entry:\"%s\"\n", rc, entry); + else + success_count++; + + entry = next_entry; + } + rc = 0; + +out: + if (data != NULL) + free(data, M_VERIEXEC); + + if (success_count == 0) + rc = EINVAL; + + return (rc); +} + +static void +parse_manifest_event(void *dummy) +{ + char *manifest_path; + char *manifest_prefix; + unsigned char *manifest_hash; + int rc; + + /* If the envs are not set fail silently */ + manifest_path = kern_getenv("veriexec.manifest_path"); + if (manifest_path == NULL) + return; + + manifest_hash = kern_getenv("veriexec.manifest_hash"); + if (manifest_hash == NULL) { + freeenv(manifest_path); + return; + } + + manifest_prefix = kern_getenv("veriexec.manifest_prefix"); + + if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH) + panic("veriexec.manifest_hash has incorrect size"); + + rc = hexstring_to_bin(manifest_hash); + if (rc != 0) + panic("mac_veriexec: veriexec.loader.manifest_hash" + " doesn't contain a hash in hexadecimal form"); + + rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix); + if (rc != 0) + panic("mac_veriexec: Failed to parse manifest err=%d", rc); + + mtx_lock(&ve_mutex); + mac_veriexec_set_state( + VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE | + VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE); + mtx_unlock(&ve_mutex); + + freeenv(manifest_path); + freeenv(manifest_hash); + if (manifest_prefix != NULL) + freeenv(manifest_prefix); +} + +EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0); Index: head/tools/build/options/WITH_LOADER_VERIEXEC_PASS_MANFIEST =================================================================== --- head/tools/build/options/WITH_LOADER_VERIEXEC_PASS_MANFIEST +++ head/tools/build/options/WITH_LOADER_VERIEXEC_PASS_MANFIEST @@ -0,0 +1,8 @@ +.\" $FreeBSD$ +Enable building +.Xr loader 8 +with support to pass a verified manifest to kernel. +Kernel has to be build with a module to parse the manfiest. +.Pp +It depends on +.Va WITH_LOADER_VERIEXEC