aboutsummaryrefslogtreecommitdiff
path: root/sbin/veriexec
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2019-02-26 06:17:23 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2019-02-26 06:17:23 +0000
commiteb12b8ea5e7fd3b2dd6386de0e96185d94f57249 (patch)
treee614b2d413571542229308b35adc3e5df66786a3 /sbin/veriexec
parenta92958df34df88e384374699c817f4a015c5b40d (diff)
downloadsrc-eb12b8ea5e7fd3b2dd6386de0e96185d94f57249.tar.gz
src-eb12b8ea5e7fd3b2dd6386de0e96185d94f57249.zip
Add verifying manifest loader for mac_veriexec
This tool will verify a signed manifest and load contents into mac_veriexec for storage Sponsored by: Juniper Networks Differential Revision: D16575
Notes
Notes: svn path=/head/; revision=344567
Diffstat (limited to 'sbin/veriexec')
-rw-r--r--sbin/veriexec/Makefile20
-rw-r--r--sbin/veriexec/Makefile.depend20
-rw-r--r--sbin/veriexec/manifest_lexer.l151
-rw-r--r--sbin/veriexec/manifest_parser.y301
-rw-r--r--sbin/veriexec/veriexec.8146
-rw-r--r--sbin/veriexec/veriexec.c177
-rw-r--r--sbin/veriexec/veriexec.h48
7 files changed, 863 insertions, 0 deletions
diff --git a/sbin/veriexec/Makefile b/sbin/veriexec/Makefile
new file mode 100644
index 000000000000..8039a8295ccd
--- /dev/null
+++ b/sbin/veriexec/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= veriexec
+MAN= veriexec.8
+SRCS= \
+ veriexec.c \
+ manifest_parser.y \
+ manifest_lexer.l
+
+LIBADD+= veriexec secureboot bearssl
+
+NO_SHARED=
+
+.include <bsd.prog.mk>
+
+CFLAGS+= -I${.CURDIR} ${XCFLAGS.${.TARGET:T:R}:U}
+
+XCFLAGS.manifest_lexer+= -Wno-missing-variable-declarations \
+ -Wno-unneeded-internal-declaration
+XCFLAGS.manifest_parser+= -Wno-missing-variable-declarations
diff --git a/sbin/veriexec/Makefile.depend b/sbin/veriexec/Makefile.depend
new file mode 100644
index 000000000000..480d2d2c255e
--- /dev/null
+++ b/sbin/veriexec/Makefile.depend
@@ -0,0 +1,20 @@
+# $FreeBSD$
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libbearssl \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libsecureboot \
+ lib/libveriexec \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/sbin/veriexec/manifest_lexer.l b/sbin/veriexec/manifest_lexer.l
new file mode 100644
index 000000000000..6c9a2cf1ae9f
--- /dev/null
+++ b/sbin/veriexec/manifest_lexer.l
@@ -0,0 +1,151 @@
+%{
+/*-
+ * Copyright (c) 2004-2018, Juniper Networks, Inc.
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <stdio.h>
+#include <string.h>
+#include "veriexec.h"
+#include "manifest_parser.h"
+
+#define YY_NO_UNPUT
+
+int lineno = 1;
+int bol = 1;
+extern int parser_version;
+
+void yyerror(const char *message);
+void warning(const char *message);
+int yylex(void);
+
+%}
+
+%%
+
+\n {
+ lineno++;
+ bol=1;
+ return EOL;
+}
+
+[/a-zA-Z0-9\.][^ \t\n=]* {
+ yylval.string = strdup(yytext);
+ if (bol) {
+ bol=0;
+ return PATH;
+ } else
+ return STRING;
+}
+
+= {
+ return EQ;
+}
+
+
+[ \t\r] ; /* eat white ones */
+
+#>[0-9]+ {
+ /*
+ * If we are older than the specified version
+ * ignore rest of line, otherwise just discard this token.
+ */
+ int skip = atoi(&yytext[2]);
+
+ VERBOSE(3, ("%s: skip if %d <= %d\n", yytext, parser_version, skip));
+ if (parser_version <= skip) {
+ /* treat as a comment, yyless() is cheaper than yyunput() */
+ yytext[yyleng - 1] = '#';
+ yyless(2);
+ }
+}
+
+#[^>\n].* ; /* comment */
+
+. yyerror("invalid character");
+
+%%
+
+static char *manifest_file = NULL;
+
+struct string_buf {
+ const char *buf;
+ size_t pos, size;
+};
+
+static int
+read_string_buf (void *token, char *dest, int count)
+{
+ struct string_buf *str_buf_p = (struct string_buf *)token;
+ ssize_t n;
+
+ if (count < 0)
+ return 0;
+
+ n = str_buf_p->size - str_buf_p->pos;
+ if (count < n)
+ n = count;
+
+ memcpy(dest, str_buf_p->buf + str_buf_p->pos, n);
+ str_buf_p->pos += n;
+
+ return n;
+}
+
+FILE *
+manifest_open (const char *file, const char *file_content)
+{
+ static struct string_buf str_buf;
+
+ if (manifest_file) {
+ free(manifest_file);
+ fclose(yyin);
+ }
+
+ str_buf.buf = file_content;
+ str_buf.pos = 0;
+ str_buf.size = strlen(file_content);
+ yyin = fropen(&str_buf, read_string_buf);
+ if (yyin) {
+ manifest_file = strdup(file);
+ lineno = 1;
+ manifest_parser_init();
+ } else
+ manifest_file = NULL;
+ return yyin;
+}
+
+void
+yyerror(const char *string)
+{
+ fprintf(stderr, "%s: %d: %s at %s\n",
+ manifest_file, lineno, string, yytext);
+}
+
+int
+yywrap(void)
+{
+ return (1);
+}
diff --git a/sbin/veriexec/manifest_parser.y b/sbin/veriexec/manifest_parser.y
new file mode 100644
index 000000000000..5889650f824b
--- /dev/null
+++ b/sbin/veriexec/manifest_parser.y
@@ -0,0 +1,301 @@
+%{
+/*-
+ * Copyright (c) 2004-2018, Juniper Networks, Inc.
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <sys/stat.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <err.h>
+#include <sysexits.h>
+#include <libsecureboot.h>
+
+#include "veriexec.h"
+
+int yylex(void);
+void yyerror(const char *);
+
+/* function prototypes */
+static int convert(char *fp, unsigned int count, unsigned char *out);
+static void do_ioctl(void);
+static int get_fingerprint_type(const char *fp_type);
+
+/* ioctl parameter struct */
+#ifdef MAXLABELLEN
+static struct verified_exec_label_params lparams;
+static struct verified_exec_params *params = &lparams.params;
+#else
+static struct verified_exec_params oparams;
+static struct verified_exec_params *params = &oparams;
+#endif
+
+#ifndef SHA256_DIGEST_LENGTH
+# define SHA_DIGEST_LENGTH br_sha1_SIZE
+# define SHA256_DIGEST_LENGTH br_sha256_SIZE
+# define SHA384_DIGEST_LENGTH br_sha384_SIZE
+#endif
+
+static int fmode;
+
+extern int lineno;
+extern int dev_fd;
+
+struct fingerprint_type {
+ const char *fp_type;
+ int fp_size;
+};
+
+/* static globals */
+static const struct fingerprint_type fingerprint_table[] = {
+ { "sha1", SHA_DIGEST_LENGTH },
+ { "sha256", SHA256_DIGEST_LENGTH },
+#if MAXFINGERPRINTLEN > 32
+ { "sha384", SHA384_DIGEST_LENGTH },
+#endif
+ { NULL, 0 }
+};
+
+/*
+ * Indicate to lexer our version.
+ * A token #>NUMBER will be consumed (and discared)
+ * by lexer if parser_version > NUMBER
+ * Otherwise the rest of the line will be discared
+ * as for a comment.
+ */
+int parser_version = 1;
+
+%}
+
+%union {
+ char *string;
+ int intval;
+}
+
+%token EOL
+%token <string> EQ
+%token <string> PATH
+%token <string> STRING
+
+%%
+
+statement: /* empty */
+ | statement path attributes eol
+ | statement error eol {
+ yyclearin; /* discard lookahead */
+ yyerrok; /* no more error */
+ fprintf(stderr,
+ "skipping to next fingerprint\n");
+ }
+ ;
+
+attributes: /* empty */
+ | attributes flag
+ | attributes attr
+ ;
+
+attr: STRING EQ STRING
+{
+ int fptype;
+
+ fptype = get_fingerprint_type($1);
+
+ /*
+ * There's only one attribute we care about
+ */
+ if (fingerprint_table[fptype].fp_size) {
+ strlcpy(params->fp_type, $1, sizeof(params->fp_type));
+ if (convert($3, fingerprint_table[fptype].fp_size,
+ params->fingerprint) < 0) {
+ yyerror("bad fingerprint");
+ YYERROR;
+ }
+ } else if (strcmp($1, "label") == 0) {
+ static int warned_labels = 0;
+
+#ifdef VERIEXEC_LABEL
+ strlcpy(lparams.label, $3, sizeof(lparams.label));
+ VERBOSE(3, ("version=%d label=%s\n", VeriexecVersion,
+ lparams.label));
+ if (VeriexecVersion > 1) {
+ params->flags |= VERIEXEC_LABEL;
+ } else
+#endif
+ if (!warned_labels) {
+ warnx("ignoring labels");
+ warned_labels = 1;
+ }
+ } else if (strcmp($1, "mode") == 0) {
+ fmode = (int)strtol($3, NULL, 8);
+ }
+};
+
+flag: STRING
+{
+ /*
+ * indirect only matters if the interpreter itself is not
+ * executable.
+ */
+ if (!strcmp($1, "indirect")) {
+ params->flags |= VERIEXEC_INDIRECT;
+ } else if (!strcmp($1, "no_ptrace")) {
+ params->flags |= VERIEXEC_NOTRACE;
+ } else if (!strcmp($1, "trusted")) {
+ params->flags |= VERIEXEC_TRUSTED;
+ } else if (!strcmp($1, "no_fips")) {
+#ifdef VERIEXEC_NOFIPS
+ params->flags |= VERIEXEC_NOFIPS;
+#endif
+ }
+}
+;
+
+path: PATH
+{
+ if (strlen($1) >= MAXPATHLEN) {
+ yyerror("Path >= MAXPATHLEN");
+ YYERROR;
+ }
+ /*
+ * The majority of files in the manifest are relative
+ * to the package mount point, but we want absolute paths.
+ * Prepending '/' is actually all we need.
+ */
+ if (snprintf(params->file, sizeof(params->file), "%s%s%s",
+ Cdir ? Cdir : "",
+ ($1[0] == '/') ? "" : "/",
+ $1) >= (int)sizeof(params->file)) {
+ errx(EX_DATAERR, "cannot form pathname");
+ }
+ params->flags = 0;
+ fmode = -1; /* unknown */
+};
+
+eol: EOL
+{
+ if (!YYRECOVERING()) { /* Don't do the ioctl if we saw an error */
+ do_ioctl();
+ }
+ params->fp_type[0] = '\0'; /* invalidate it */
+};
+
+%%
+
+void
+manifest_parser_init(void)
+{
+ params->fp_type[0] = '\0'; /* invalidate it */
+}
+
+int
+get_fingerprint_type(const char *fp_type)
+{
+ int i;
+
+ for (i = 0; fingerprint_table[i].fp_type; i++)
+ if (!strcmp(fp_type, fingerprint_table[i].fp_type))
+ break;
+
+ return (i);
+}
+
+/*
+ * Convert: takes the hexadecimal string pointed to by fp and converts
+ * it to a "count" byte binary number which is stored in the array pointed to
+ * by out. Returns -1 if the conversion fails.
+ */
+static int
+convert(char *fp, unsigned int count, unsigned char *out)
+{
+ unsigned int i;
+ int value;
+
+ for (i = 0; i < count; i++) {
+ value = 0;
+ if (isdigit(fp[i * 2]))
+ value += fp[i * 2] - '0';
+ else if (isxdigit(fp[i * 2]))
+ value += 10 + tolower(fp[i * 2]) - 'a';
+ else
+ return (-1);
+ value <<= 4;
+ if (isdigit(fp[i * 2 + 1]))
+ value += fp[i * 2 + 1] - '0';
+ else if (isxdigit(fp[i * 2 + 1]))
+ value += 10 + tolower(fp[i * 2 + 1]) - 'a';
+ else
+ return (-1);
+ out[i] = value;
+ }
+
+ return (i);
+}
+
+/*
+ * Perform the load of the fingerprint. Assumes that the fingerprint
+ * pseudo-device is opened and the file handle is in fd.
+ */
+static void
+do_ioctl(void)
+{
+ struct stat st;
+
+ if (params->fp_type[0] == '\0') {
+ VERBOSE(1,("skipping %s\n", params->file));
+ return;
+ }
+
+ /*
+ * See if the path is executable, if not put it on the FILE list.
+ */
+ if (fmode > 0) {
+ if (!(fmode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ params->flags |= VERIEXEC_FILE;
+ }
+ } else if (stat(params->file, &st) == 0) {
+ if (!(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
+ params->flags |= VERIEXEC_FILE;
+ }
+ }
+ /*
+ * We may be forcing some flags...
+ */
+ params->flags |= ForceFlags;
+ VERBOSE(1, ("loading %s for %s %s flags=%#x\n",
+ params->fp_type,
+ (params->flags == VERIEXEC_FILE) ? "file" : "executable",
+ params->file, params->flags));
+
+#ifdef VERIEXEC_LABEL
+ if (params->flags & VERIEXEC_LABEL) {
+ if (ioctl(dev_fd, VERIEXEC_LABEL_LOAD, &lparams) < 0)
+ warn("cannot update veriexec label for %s",
+ params->file);
+ } else
+#endif
+ if (ioctl(dev_fd, VERIEXEC_SIGNED_LOAD, params) < 0)
+ warn("cannot update veriexec for %s", params->file);
+ params->fp_type[0] = '\0';
+}
diff --git a/sbin/veriexec/veriexec.8 b/sbin/veriexec/veriexec.8
new file mode 100644
index 000000000000..83cad9a3e173
--- /dev/null
+++ b/sbin/veriexec/veriexec.8
@@ -0,0 +1,146 @@
+.\"-
+.\" Copyright (c) 2018, Juniper Networks, Inc.
+.\"
+.\" 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" "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 COPYRIGHT
+.\" OWNER OR CONTRIBUTORS 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$
+.\"
+.Dd July 4, 2018
+.Dt VERIEXEC 8
+.Os
+.Sh NAME
+.Nm veriexec
+.Nd manipulate state of mac_veriexec
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl C Ar directory
+.Pa manifest
+.Nm
+.Fl z Ar state
+.Nm
+.Fl i Ar state
+.Nm
+.Fl x
+.Ar file ...
+.Sh DESCRIPTION
+.Nm
+is a utility to query or manipulate the state of
+.Xr mac_veriexec 4 .
+.Pp
+The first form is for loading a
+.Pa manifest .
+.Nm
+first verifies a digital signature of the
+.Ar manifest
+and if successful, parses it and feeds its content to kernel.
+.Pp
+The second form with
+.Fl z
+is used to modify the
+.Ar state ,
+and with
+.Fl i
+to query the current
+.Ar state .
+.Pp
+The final form with
+.Fl x
+is used to test whether
+.Ar file
+is verified or not.
+This requires
+.Xr mac_veriexec 4
+to be in the
+.Ql active
+or
+.Ql enforce
+state.
+.Pp
+The possible states
+are:
+.Bl -tag -width enforce
+.It Ar loaded
+set automatically when first
+.Pa manifest
+has been loaded.
+.It Ar active
+.Xr mac_veriexec 4
+will begin checking files.
+This state can only be entered from the
+.Ar loaded
+state.
+.It Ar enforce
+.Xr mac_veriexec 4
+will fail attempts to
+.Xr exec 2
+or
+.Xr open 2
+files with
+.Dv O_VERIFY
+unless verified.
+.It Ar locked
+prevent loading of any more manifests.
+.El
+.Sh MANIFESTS
+The manifest contains a mapping of relative pathnames to fingerprints
+with optional flags.
+For example:
+.Bd -literal -offset indent
+sbin/veriexec sha256=f22136...c0ff71 no_ptrace
+usr/bin/python sha256=5944d9...876525 indirect
+sbin/somedaemon sha256=77fc2f...63f5687 label=mod1/val1,mod2/val2
+.Ed
+The supported flags are:
+.Bl -tag -width indirect
+.It Ql indirect
+the executable cannot be run directly,
+but can be used as an interpreter for example via:
+.Bd -literal -offset indent
+#!/usr/bin/python
+.Ed
+.It Ql no_ptrace
+do not allow running executable under a debugger.
+Useful for any application critical to the security state of system.
+.El
+.Pp
+The
+.Ql label
+argument allows associating a
+.Xr maclabel 7
+with the executable.
+Neither
+.Nm
+nor
+.Xr mac_veriexec 4
+(if it supports labels)
+pay any attention to the content of the label
+they are provided for the use of other
+.Xr mac 4
+modules.
+.Sh HISTORY
+The Verified Exec system first appeared in NetBSD.
+This utility derrives from the one found in Junos.
+The key difference is the requirement that manifest files
+be digitally signed.
+
+
diff --git a/sbin/veriexec/veriexec.c b/sbin/veriexec/veriexec.c
new file mode 100644
index 000000000000..b9299efaad02
--- /dev/null
+++ b/sbin/veriexec/veriexec.c
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2018, Juniper Networks, Inc.
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <paths.h>
+#include <err.h>
+#include <syslog.h>
+#include <libsecureboot.h>
+#include <libveriexec.h>
+
+#include "veriexec.h"
+
+int dev_fd = -1;
+int ForceFlags = 0;
+int Verbose = 0;
+int VeriexecVersion = 0;
+
+const char *Cdir = NULL;
+
+static int
+veriexec_load(const char *manifest)
+{
+ unsigned char *content;
+ int rc;
+
+ content = verify_signed(manifest, VEF_VERBOSE);
+ if (!content)
+ errx(EX_USAGE, "cannot verify %s", manifest);
+ if (manifest_open(manifest, content)) {
+ rc = yyparse();
+ } else {
+ err(EX_NOINPUT, "cannot load %s", manifest);
+ }
+ free(content);
+ return (rc);
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned long ctl;
+ int c;
+ int x;
+
+ dev_fd = open(_PATH_DEV_VERIEXEC, O_WRONLY, 0);
+
+ while ((c = getopt(argc, argv, "C:i:x:vz:")) != -1) {
+ switch (c) {
+ case 'C':
+ Cdir = optarg;
+ break;
+ case 'i':
+ if (dev_fd < 0) {
+ err(EX_UNAVAILABLE, "cannot open veriexec");
+ }
+ if (ioctl(dev_fd, VERIEXEC_GETSTATE, &x)) {
+ err(EX_UNAVAILABLE,
+ "Cannot get veriexec state");
+ }
+ switch (optarg[0]) {
+ case 'a': /* active */
+ ctl = VERIEXEC_STATE_ACTIVE;
+ break;
+ case 'e': /* enforce */
+ ctl = VERIEXEC_STATE_ENFORCE;
+ break;
+ case 'l': /* loaded/locked */
+ ctl = (strncmp(optarg, "lock", 4)) ?
+ VERIEXEC_STATE_LOCKED :
+ VERIEXEC_STATE_LOADED;
+ break;
+ default:
+ errx(EX_USAGE, "unknown state %s", optarg);
+ break;
+ }
+ exit((x & ctl) == 0);
+ break;
+ case 'v':
+ Verbose++;
+ break;
+ case 'x':
+ /*
+ * -x says all other args are paths to check.
+ */
+ for (x = 0; optind < argc; optind++) {
+ if (veriexec_check_path(argv[optind])) {
+ warn("%s", argv[optind]);
+ x = 2;
+ }
+ }
+ exit(x);
+ break;
+ case 'z':
+ switch (optarg[0]) {
+ case 'a': /* active */
+ ctl = VERIEXEC_ACTIVE;
+ break;
+ case 'd': /* debug* */
+ ctl = (strstr(optarg, "off")) ?
+ VERIEXEC_DEBUG_OFF : VERIEXEC_DEBUG_ON;
+ if (optind < argc && ctl == VERIEXEC_DEBUG_ON) {
+ x = atoi(argv[optind]);
+ if (x == 0)
+ ctl = VERIEXEC_DEBUG_OFF;
+ }
+ break;
+ case 'e': /* enforce */
+ ctl = VERIEXEC_ENFORCE;
+ break;
+ case 'g':
+ ctl = VERIEXEC_GETSTATE; /* get state */
+ break;
+ case 'l': /* lock */
+ ctl = VERIEXEC_LOCK;
+ break;
+ default:
+ errx(EX_USAGE, "unknown command %s", optarg);
+ break;
+ }
+ if (dev_fd < 0) {
+ err(EX_UNAVAILABLE, "cannot open veriexec");
+ }
+ if (ioctl(dev_fd, ctl, &x)) {
+ err(EX_UNAVAILABLE, "cannot %s veriexec", optarg);
+ }
+ if (ctl == VERIEXEC_DEBUG_ON ||
+ ctl == VERIEXEC_DEBUG_OFF) {
+ printf("debug is: %d\n", x);
+ } else if (ctl == VERIEXEC_GETSTATE) {
+ printf("%#o\n", x);
+ }
+ exit(EX_OK);
+ break;
+ }
+ }
+ openlog(getprogname(), LOG_PID, LOG_AUTH);
+ if (ve_trust_init() < 1)
+ errx(EX_OSFILE, "cannot initialize trust store");
+#ifdef VERIEXEC_GETVERSION
+ if (ioctl(dev_fd, VERIEXEC_GETVERSION, &VeriexecVersion)) {
+ VeriexecVersion = 0; /* unknown */
+ }
+#endif
+
+ for (; optind < argc; optind++) {
+ if (veriexec_load(argv[optind])) {
+ err(EX_DATAERR, "cannot load %s", argv[optind]);
+ }
+ }
+ exit(EX_OK);
+}
diff --git a/sbin/veriexec/veriexec.h b/sbin/veriexec/veriexec.h
new file mode 100644
index 000000000000..b06025f47e90
--- /dev/null
+++ b/sbin/veriexec/veriexec.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2018, Juniper Networks, Inc.
+ *
+ * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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$
+ */
+
+#ifndef __VERIEXEC_H__
+#define __VERIEXEC_H__
+
+#include <sys/ioctl.h>
+#include <dev/veriexec/veriexec_ioctl.h>
+
+extern int dev_fd;
+extern int parser_version;
+extern int ForceFlags;
+extern int Verbose;
+extern int VeriexecVersion;
+extern const char *Cdir;
+
+#define VERBOSE(n, x) if (Verbose > n) printf x
+
+FILE * manifest_open (const char *file, const char *file_content);
+void manifest_parser_init(void);
+int yyparse(void);
+extern FILE *yyin;
+
+#endif