git: 666554111a7e - main - Update libsecureboot

From: Simon J. Gerraty <sjg_at_FreeBSD.org>
Date: Mon, 18 Apr 2022 19:54:22 UTC
The branch main has been updated by sjg:

URL: https://cgit.FreeBSD.org/src/commit/?id=666554111a7e6b4c1a9a6ff2e73f12cd582573bb

commit 666554111a7e6b4c1a9a6ff2e73f12cd582573bb
Author:     Simon J. Gerraty <sjg@FreeBSD.org>
AuthorDate: 2022-04-18 19:53:53 +0000
Commit:     Simon J. Gerraty <sjg@FreeBSD.org>
CommitDate: 2022-04-18 19:54:15 +0000

    Update libsecureboot
    
    Preparation for updating bearssl, pull in updates to libsecureboot.
    
    o fix handling of some out-of-memory cases
    
    o allow more control over reporting of Verified/Unverified files.
      this helps boot time when console output is slow
    
      o recheck verbose/debug level after reading any unverified file
    
    o more debug support for vectx
    
    o hash_string to support fake stat for tftp
    
    o tests/tvo add -v to simply verify signatures
    
    o vets.c allow for HAVE_BR_X509_TIME_CHECK which will greatly simplify
      verification in loader
    
    o report date when certificate fails validity period checks
    
    Reviewed by: stevek
    Sponsored by: Juniper Networks, Inc.
---
 lib/libsecureboot/h/libsecureboot.h  |   3 +
 lib/libsecureboot/h/verify_file.h    |  11 +-
 lib/libsecureboot/openpgp/opgp_sig.c |   4 +-
 lib/libsecureboot/readfile.c         |  38 ++++---
 lib/libsecureboot/tests/tvo.c        |  45 +++++---
 lib/libsecureboot/vectx.c            |  50 +++++++--
 lib/libsecureboot/veopen.c           |  13 ++-
 lib/libsecureboot/verify_file.c      | 208 +++++++++++++++++++++++++++--------
 lib/libsecureboot/vets.c             | 113 ++++++++++++++++++-
 9 files changed, 390 insertions(+), 95 deletions(-)

diff --git a/lib/libsecureboot/h/libsecureboot.h b/lib/libsecureboot/h/libsecureboot.h
index 79b5cc46ee97..200f8bdb763f 100644
--- a/lib/libsecureboot/h/libsecureboot.h
+++ b/lib/libsecureboot/h/libsecureboot.h
@@ -48,8 +48,11 @@ unsigned char * read_file(const char *, size_t *);
 #endif
 
 extern int DebugVe;
+extern int VerifyFlags;
 
+#ifndef DEBUG_PRINTF
 #define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x
+#endif
 
 int ve_trust_init(void);
 size_t ve_trust_anchors_add_buf(unsigned char *, size_t);
diff --git a/lib/libsecureboot/h/verify_file.h b/lib/libsecureboot/h/verify_file.h
index 844b8266f42c..88d758b27af4 100644
--- a/lib/libsecureboot/h/verify_file.h
+++ b/lib/libsecureboot/h/verify_file.h
@@ -21,8 +21,6 @@
  * 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 _VERIFY_FILE_H_
 #define _VERIFY_FILE_H_
@@ -37,6 +35,11 @@
 #define VE_UNVERIFIED_OK 0           /* not verified but that's ok */
 #define VE_NOT_VERIFYING 2	     /* we are not verifying */
 
+/* suitable buf size for hash_string */
+#ifndef SHA_DIGEST_LENGTH
+# define SHA_DIGEST_LENGTH 20
+#endif
+
 struct stat;
 
 int	verify_prep(int, const char *, off_t, struct stat *, const char *);
@@ -47,8 +50,12 @@ int	ve_status_get(int);
 int	load_manifest(const char *, const char *, const char *, struct stat *);
 int	pass_manifest(const char *, const char *);
 int	pass_manifest_export_envs(void);
+void	verify_report(const char *, int, int, struct stat *);
 int	verify_file(int, const char *, off_t, int, const char *);
 void	verify_pcr_export(void);
+int	hash_string(char *s, size_t n, char *buf, size_t bufsz);
+int	is_verified(struct stat *);
+void	add_verify_status(struct stat *, int);
 
 struct vectx;
 struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *, const char *);
diff --git a/lib/libsecureboot/openpgp/opgp_sig.c b/lib/libsecureboot/openpgp/opgp_sig.c
index c16d8974e8a3..fcf4a708c4c3 100644
--- a/lib/libsecureboot/openpgp/opgp_sig.c
+++ b/lib/libsecureboot/openpgp/opgp_sig.c
@@ -369,7 +369,7 @@ openpgp_verify(const char *filename,
 #endif
 
 			if (rc > 0) {
-				if ((flags & 1))
+				if ((flags & VEF_VERBOSE))
 					printf("Verified %s signed by %s\n",
 					    filename,
 					    key->user ? key->user->name : "someone");
@@ -447,7 +447,7 @@ openpgp_verify_file(const char *filename, unsigned char *fdata, size_t nbytes)
 		return (-1);
 	}
 	sdata = read_file(sname, &sz);
-	return (openpgp_verify(filename, fdata, nbytes, sdata, sz, 1));
+	return (openpgp_verify(filename, fdata, nbytes, sdata, sz, VerifyFlags));
 }
 #endif
 
diff --git a/lib/libsecureboot/readfile.c b/lib/libsecureboot/readfile.c
index ff9f9f6f6d60..550c9419c159 100644
--- a/lib/libsecureboot/readfile.c
+++ b/lib/libsecureboot/readfile.c
@@ -34,20 +34,22 @@ read_fd(int fd, size_t len)
 	unsigned char *buf;
 
 	buf = malloc(len + 1);
-	for (x = 0, m = len; m > 0; ) {
-		n = read(fd, &buf[x], m);
-		if (n < 0)
-			break;
-		if (n > 0) {
-			m -= n;
-			x += n;
+	if (buf != NULL) {
+		for (x = 0, m = len; m > 0; ) {
+			n = read(fd, &buf[x], m);
+			if (n < 0)
+				break;
+			if (n > 0) {
+				m -= n;
+				x += n;
+			}
 		}
+		if (m == 0) {
+			buf[len] = '\0';
+			return (buf);
+		}
+		free(buf);
 	}
-	if (m == 0) {
-		buf[len] = '\0';
-		return (buf);
-	}
-	free(buf);
 	return (NULL);
 }
 
@@ -65,8 +67,16 @@ read_file(const char *path, size_t *len)
 	fstat(fd, &st);
 	ucp = read_fd(fd, st.st_size);
 	close(fd);
-	if (len != NULL && ucp != NULL)
-		*len = st.st_size;
+	if (ucp != NULL) {
+		if (len != NULL)
+			*len = st.st_size;
+	}
+#ifdef _STANDALONE
+	else
+		printf("%s: out of memory! %lu\n", __func__,
+		    (unsigned long)len);
+#endif
+
 	return (ucp);
 }
 
diff --git a/lib/libsecureboot/tests/tvo.c b/lib/libsecureboot/tests/tvo.c
index 879d87f128f1..5ea471faa0ba 100644
--- a/lib/libsecureboot/tests/tvo.c
+++ b/lib/libsecureboot/tests/tvo.c
@@ -31,6 +31,12 @@ __FBSDID("$FreeBSD$");
 #include <err.h>
 #include <verify_file.h>
 
+/* keep clang quiet */
+extern char *Destdir;
+extern size_t DestdirLen;
+extern char *Skip;
+extern time_t ve_utc;
+
 size_t DestdirLen;
 char *Destdir;
 char *Skip;
@@ -42,9 +48,9 @@ main(int argc, char *argv[])
 	int fd;
 	int c;
 	int Vflag;
+	int vflag;
 	char *cp;
 	char *prefix;
-	char *destdir;
 
 	Destdir = NULL;
 	DestdirLen = 0;
@@ -52,10 +58,10 @@ main(int argc, char *argv[])
 	Skip = NULL;
 
 	n = ve_trust_init();
-	printf("Trust %d\n", n);
 	Vflag = 0;
+	vflag = 0;
 
-	while ((c = getopt(argc, argv, "D:dp:s:T:V")) != -1) {
+	while ((c = getopt(argc, argv, "D:dp:s:T:u:Vv")) != -1) {
 		switch (c) {
 		case 'D':
 			Destdir = optarg;
@@ -77,17 +83,25 @@ main(int argc, char *argv[])
 		case 'V':
 			Vflag = 1;
 			break;
+		case 'v':
+			vflag = 1;
+			break;
+		case 'u':
+			ve_utc = (time_t)atoi(optarg);
+			break;
 		default:
 			errx(1, "unknown option: -%c", c);
 			break;
 		}
 	}
 
+	if (!vflag) {
+		printf("Trust %d\n", n);
 #ifdef VE_PCR_SUPPORT
-	ve_pcr_updating_set(1);
+		ve_pcr_updating_set(1);
 #endif
-	ve_self_tests();
-
+		ve_self_tests();
+	}
 	for ( ; optind < argc; optind++) {
 		if (Vflag) {
 			/*
@@ -113,8 +127,9 @@ main(int argc, char *argv[])
 			if (cp) {
 				printf("Verified: %s: %.28s...\n",
 				    argv[optind], cp);
-				fingerprint_info_add(argv[optind],
-				    prefix, Skip, cp, NULL);
+				if (!vflag)
+				    fingerprint_info_add(argv[optind],
+					prefix, Skip, cp, NULL);
 			} else {
 				fprintf(stderr, "%s: %s\n",
 				    argv[optind], ve_error_get());
@@ -126,8 +141,9 @@ main(int argc, char *argv[])
 			if (cp) {
 				printf("Verified: %s: %.28s...\n",
 				    argv[optind], cp);
-				fingerprint_info_add(argv[optind],
-				    prefix, Skip, cp, NULL);
+				if (!vflag)
+					fingerprint_info_add(argv[optind],
+					    prefix, Skip, cp, NULL);
 			} else {
 				fprintf(stderr, "%s: %s\n",
 				    argv[optind], ve_error_get());
@@ -150,7 +166,8 @@ main(int argc, char *argv[])
 				char buf[BUFSIZ];
 				struct stat st;
 				int error;
-				size_t off, n;
+				off_t off;
+				size_t nb;
 
 				fstat(fd, &st);
 				lseek(fd, 0, SEEK_SET);
@@ -167,10 +184,10 @@ main(int argc, char *argv[])
 					/* we can seek backwards! */
 					off = vectx_lseek(vp, off/2, SEEK_SET);
 					if (off < st.st_size) {
-						n = vectx_read(vp, buf,
+						nb = vectx_read(vp, buf,
 						    sizeof(buf));
-						if (n > 0)
-							off += n;
+						if (nb > 0)
+							off += nb;
 					}
 					off = vectx_lseek(vp, 0, SEEK_END);
 					/* repeating that should be harmless */
diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c
index 3e5277d935f0..45a15ddece4b 100644
--- a/lib/libsecureboot/vectx.c
+++ b/lib/libsecureboot/vectx.c
@@ -32,6 +32,11 @@ __FBSDID("$FreeBSD$");
 #undef _KERNEL
 #endif
 
+#ifdef VECTX_DEBUG
+static int vectx_debug = VECTX_DEBUG;
+# define DEBUG_PRINTF(n, x) if (vectx_debug >= n) printf x
+#endif
+
 #include "libsecureboot-priv.h"
 #include <verify_file.h>
 
@@ -52,10 +57,11 @@ struct vectx {
 	const char	*vec_want;	/* hash value we want */
 	off_t		vec_off;	/* current offset */
 	off_t		vec_hashed;	/* where we have hashed to */
-	size_t		vec_size;	/* size of path */
+	off_t		vec_size;	/* size of path */
 	size_t		vec_hashsz;	/* size of hash */
 	int		vec_fd;		/* file descriptor */
 	int		vec_status;	/* verification status */
+	int		vec_closing;	/* we are closing */
 };
 
 
@@ -125,6 +131,7 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp,
 	ctx->vec_want = NULL;
 	ctx->vec_status = 0;
 	ctx->vec_hashsz = hashsz = 0;
+	ctx->vec_closing = 0;
 
 	if (rc == 0) {
 		/* we are not verifying this */
@@ -229,6 +236,12 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
 		x = nbytes - off;
 		x = MIN(PAGE_SIZE, x);
 		d = n = read(ctx->vec_fd, &bp[off], x);
+		if (ctx->vec_closing && n < x) {
+			DEBUG_PRINTF(3,
+			    ("%s: read %d off=%ld hashed=%ld size=%ld\n",
+			     __func__, n, (long)ctx->vec_off,
+			     (long)ctx->vec_hashed, (long)ctx->vec_size));
+		}
 		if (n < 0) {
 			return (n);
 		}
@@ -242,6 +255,12 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
 				ctx->vec_off += x;
 			}
 			if (d > 0) {
+				if (ctx->vec_closing && d < PAGE_SIZE) {
+					DEBUG_PRINTF(3,
+					    ("%s: update %ld + %d\n",
+						__func__,
+						(long)ctx->vec_hashed, d));
+				}
 				ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], d);
 				off += d;
 				ctx->vec_off += d;
@@ -286,7 +305,14 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence)
 	/*
 	 * Convert whence to SEEK_SET
 	 */
+	DEBUG_PRINTF(3,
+	    ("%s(%s, %ld, %d)\n", __func__, ctx->vec_path, (long)off, whence));
 	if (whence == SEEK_END && off <= 0) {
+		if (ctx->vec_closing && ctx->vec_hashed < ctx->vec_size) {
+			DEBUG_PRINTF(3, ("%s: SEEK_END %ld\n",
+				__func__,
+				(long)(ctx->vec_size - ctx->vec_hashed)));
+		}
 		whence = SEEK_SET;
 		off += ctx->vec_size;
 	} else if (whence == SEEK_CUR) {
@@ -294,12 +320,22 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence)
 		off += ctx->vec_off;
 	}
 	if (whence != SEEK_SET ||
-	    (size_t)off > ctx->vec_size) {
-		printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n",
-		    __func__, whence, (long long)ctx->vec_off, (long long)off);
+	    off > ctx->vec_size) {
+		printf("ERROR: %s: unsupported operation: whence=%d off=%ld -> %ld\n",
+		    __func__, whence, (long)ctx->vec_off, (long)off);
 		return (-1);
 	}
 	if (off < ctx->vec_hashed) {
+#ifdef _STANDALONE
+		struct open_file *f = fd2open_file(ctx->vec_fd);
+
+		if (f != NULL &&
+		    strncmp(f->f_ops->fs_name, "tftp", 4) == 0) {
+			/* we cannot rewind if we've hashed much of the file */
+			if (ctx->vec_hashed > ctx->vec_size / 5)
+				return (-1);	/* refuse! */
+		}
+#endif
 		/* seeking backwards! just do it */
 		ctx->vec_off = lseek(ctx->vec_fd, off, whence);
 		return (ctx->vec_off);
@@ -337,6 +373,7 @@ vectx_close(struct vectx *ctx, int severity, const char *caller)
 {
 	int rc;
 
+	ctx->vec_closing = 1;
 	if (ctx->vec_hashsz == 0) {
 		rc = ctx->vec_status;
 	} else {
@@ -356,16 +393,13 @@ vectx_close(struct vectx *ctx, int severity, const char *caller)
 	DEBUG_PRINTF(2,
 	    ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
 		caller,ctx->vec_path, rc, severity));
+	verify_report(ctx->vec_path, severity, rc, NULL);
 	if (rc == VE_FINGERPRINT_WRONG) {
-		printf("Unverified: %s\n", ve_error_get());
 #if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
 		/* we are generally called with VE_MUST */
 		if (severity > VE_WANT)
 			panic("cannot continue");
 #endif
-	} else if (severity > VE_WANT) {
-		printf("%serified %s\n", (rc <= 0) ? "Unv" : "V",
-		    ctx->vec_path);
 	}
 	free(ctx);
 	return ((rc < 0) ? rc : 0);
diff --git a/lib/libsecureboot/veopen.c b/lib/libsecureboot/veopen.c
index da6291504c4c..b428fa4b13a3 100644
--- a/lib/libsecureboot/veopen.c
+++ b/lib/libsecureboot/veopen.c
@@ -77,6 +77,13 @@ fingerprint_info_add(const char *filename, const char *prefix,
 
 	fingerprint_info_init();
 	nfip = malloc(sizeof(struct fingerprint_info));
+	if (nfip == NULL) {
+#ifdef _STANDALONE
+		printf("%s: out of memory! %lu\n", __func__,
+		    (unsigned long)sizeof(struct fingerprint_info));
+#endif
+		return;
+	}
 	if (prefix) {
 		nfip->fi_prefix = strdup(prefix);
 	} else {
@@ -115,10 +122,9 @@ fingerprint_info_add(const char *filename, const char *prefix,
 		if (n == 0)
 			break;
 	}
+	nfip->fi_dev = stp->st_dev;
 #ifdef UNIT_TEST
 	nfip->fi_dev = 0;
-#else
-	nfip->fi_dev = stp->st_dev;
 #endif
 	nfip->fi_data = data;
 	nfip->fi_prefix_len = strlen(nfip->fi_prefix);
@@ -198,9 +204,10 @@ fingerprint_info_lookup(int fd, const char *path)
 	n = strlcpy(pbuf, path, sizeof(pbuf));
 	if (n >= sizeof(pbuf))
 		return (NULL);
-#ifndef UNIT_TEST
 	if (fstat(fd, &st) == 0)
 		dev = st.st_dev;
+#ifdef UNIT_TEST
+	dev = 0;
 #endif
 	/*
 	 * get the first entry - it will have longest prefix
diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c
index 22f3f06b0eda..a6d4410a5724 100644
--- a/lib/libsecureboot/verify_file.c
+++ b/lib/libsecureboot/verify_file.c
@@ -57,14 +57,27 @@ extern char *Skip;
  * The extra slot is for tracking most recently opened.
  */
 #ifndef SOPEN_MAX
-#define	SOPEN_MAX	64
+#define SOPEN_MAX       64
 #endif
 static int ve_status[SOPEN_MAX+1];
 static int ve_status_state;
 struct verify_status;
-struct verify_status *verified_files = NULL;
+static struct verify_status *verified_files = NULL;
 static int loaded_manifests = 0;	/* have we loaded anything? */
 
+enum {
+	VE_VERBOSE_SILENT,		/* only report errors */
+	VE_VERBOSE_UNVERIFIED,		/* all unverified files */
+	VE_VERBOSE_MUST,		/* report VE_MUST */
+	VE_VERBOSE_ALL,			/* report all */
+	VE_VERBOSE_DEBUG,		/* extra noise */
+};
+
+#ifndef VE_VERBOSE_DEFAULT
+# define VE_VERBOSE_DEFAULT VE_VERBOSE_MUST
+#endif
+static int Verbose = VE_VERBOSE_DEFAULT;
+
 #define VE_STATUS_NONE	1
 #define VE_STATUS_VALID	2
 
@@ -138,11 +151,13 @@ add_verify_status(struct stat *stp, int status)
 	struct verify_status *vsp;
 
 	vsp = malloc(sizeof(struct verify_status));
-	vsp->vs_next = verified_files;
-	vsp->vs_dev = stp->st_dev;
-	vsp->vs_ino = stp->st_ino;
-	vsp->vs_status = status;
-	verified_files = vsp;
+	if (vsp) {
+		vsp->vs_next = verified_files;
+		vsp->vs_dev = stp->st_dev;
+		vsp->vs_ino = stp->st_ino;
+		vsp->vs_status = status;
+		verified_files = vsp;
+	}
 }
 
 
@@ -173,7 +188,7 @@ load_manifest(const char *name, const char *prefix,
 		}
 		/* loader has no sense of time */
 		ve_utc_set(stp->st_mtime);
-		content = (char *)verify_signed(name, VEF_VERBOSE);
+		content = (char *)verify_signed(name, VerifyFlags);
 		if (content) {
 #ifdef UNIT_TEST
 			if (DestdirLen > 0 &&
@@ -216,6 +231,11 @@ find_manifest(const char *name)
 	rc = VE_FINGERPRINT_NONE;
 	for (tp = manifest_names; *tp; tp++) {
 		snprintf(buf, sizeof(buf), "%s/%s", prefix, *tp);
+		if (*tp[0] == '.') {
+			/* skip /../ */
+			if (prefix[0] == '\0' || prefix[1] == '\0')
+				continue;
+		}
 		DEBUG_PRINTF(5, ("looking for %s\n", buf));
 		if (stat(buf, &st) == 0 && st.st_size > 0) {
 #ifdef MANIFEST_SKIP_ALWAYS		/* very unlikely */
@@ -243,20 +263,21 @@ find_manifest(const char *name)
 #else
 # define ACCEPT_NO_FP_DEFAULT	VE_MUST
 #endif
-#ifndef VE_VERBOSE_DEFAULT
-# define VE_VERBOSE_DEFAULT	0
-#endif
 
 static int
 severity_guess(const char *filename)
 {
 	const char *cp;
 
-	/* Some files like *.conf and *.hints may be unsigned */
+	/*
+	 * Some files like *.conf and *.hints may be unsigned,
+	 * a *.tgz is expected to have its own signed manifest.
+	 */
 	if ((cp = strrchr(filename, '.'))) {
 		if (strcmp(cp, ".conf") == 0 ||
 		    strcmp(cp, ".cookie") == 0 ||
-			strcmp(cp, ".hints") == 0)
+		    strcmp(cp, ".hints") == 0 ||
+		    strcmp(cp, ".tgz") == 0)
 			return (VE_TRY);
 		if (strcmp(cp, ".4th") == 0 ||
 		    strcmp(cp, ".lua") == 0 ||
@@ -270,15 +291,14 @@ static int Verifying = -1;		/* 0 if not verifying */
 
 static void
 verify_tweak(int fd, off_t off, struct stat *stp,
-    char *tweak, int *accept_no_fp,
-    int *verbose)
+    char *tweak, int *accept_no_fp)
 {
 	if (strcmp(tweak, "off") == 0) {
 		Verifying = 0;
 	} else if (strcmp(tweak, "strict") == 0) {
 		/* anything caller wants verified must be */
 		*accept_no_fp = VE_WANT;
-		*verbose = 1; /* warn of anything unverified */
+		Verbose = VE_VERBOSE_ALL;
 		/* treat self test failure as fatal */
 		if (!ve_self_tests()) {
 			panic("verify self tests failed");
@@ -290,9 +310,13 @@ verify_tweak(int fd, off_t off, struct stat *stp,
 		/* best effort: always accept no fp */
 		*accept_no_fp = VE_MUST + 1;
 	} else if (strcmp(tweak, "verbose") == 0) {
-		*verbose = 1;
+		Verbose = VE_VERBOSE_ALL;
 	} else if (strcmp(tweak, "quiet") == 0) {
-		*verbose = 0;
+		Verbose = VE_VERBOSE_UNVERIFIED;
+		VerifyFlags = 0;
+	} else if (strcmp(tweak, "silent") == 0) {
+		Verbose = VE_VERBOSE_SILENT;
+		VerifyFlags = 0;
 	} else if (strncmp(tweak, "trust", 5) == 0) {
 		/* content is trust anchor to add or revoke */
 		unsigned char *ucp;
@@ -338,6 +362,68 @@ getenv_int(const char *var, int def)
 }
 
 
+/**
+ * @brief report verification status
+ *
+ * @param[in] path
+ *	path we attempted to verify
+ *
+ * @param[in] severity
+ * 	indicator of how to handle case of missing fingerprint
+ *
+ * @param[in] status
+ *	result of verification
+ *	0 not a file to be verified, > 0 success, < 0 error
+ *
+ * @param[in] stp
+ *	pointer to struct stat, used in extra info to be output
+ *
+ * The output is dictated by combinations of the above and the setting
+ * of Verbose:
+ *
+ * VE_VERBOSE_SILENT
+ * 	report only failure to verify if severity is VE_WANT or higher.
+ *
+ * VE_VERBOSE_UNVERIFIED
+ * 	report any unverified file.
+ *
+ * VE_VERBOSE_MUST
+ * 	report verified only if severity is VE_MUST or higher.
+ *
+ * VE_VERBOSE_ALL
+ * 	report all verified files.
+ *
+ * VE_VERBOSE_DEBUG
+ * 	if stp is not NULL report dev,inode for path
+ */
+void
+verify_report(const char *path, int severity, int status, struct stat *stp)
+{
+	if (status < 0 || status == VE_FINGERPRINT_IGNORE) {
+		if (Verbose >= VE_VERBOSE_UNVERIFIED || severity > VE_TRY ||
+		    status <= VE_FINGERPRINT_WRONG) {
+			if (Verbose == VE_VERBOSE_DEBUG && stp != NULL)
+				printf("Unverified %s %llu,%llu\n",
+				    ve_error_get(),
+				    (long long)stp->st_dev,
+				    (long long)stp->st_ino);
+			else
+				printf("Unverified %s\n", ve_error_get());
+		}
+	} else if (status > 0 && Verbose >= VE_VERBOSE_MUST) {
+		if (severity >= VE_MUST || Verbose >= VE_VERBOSE_ALL) {
+			if (Verbose == VE_VERBOSE_DEBUG && stp != NULL)
+				printf("Unverified %s %llu,%llu\n",
+				    path,
+				    (long long)stp->st_dev,
+				    (long long)stp->st_ino);
+			else
+				printf("Verified %s\n", path);
+		}
+	}
+}
+
+
 /**
  * @brief prepare to verify an open file
  *
@@ -358,9 +444,6 @@ verify_prep(int fd, const char *filename, off_t off, struct stat *stp,
 
 	if (Verifying < 0) {
 		Verifying = ve_trust_init();
-#ifndef UNIT_TEST
-		ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL));
-#endif
 		/* initialize ve_status with default result */
 		rc = Verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING;
 		ve_status_set(0, rc);
@@ -377,9 +460,9 @@ verify_prep(int fd, const char *filename, off_t off, struct stat *stp,
 			return (0);
 	}
 	DEBUG_PRINTF(2,
-	    ("verify_prep: caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n",
+	    ("verify_prep: caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%llu\n",
 		caller, fd, filename, (long long)off, (long long)stp->st_dev,
-		(long long)stp->st_ino));
+		(unsigned long long)stp->st_ino));
 	rc = is_verified(stp);
 	DEBUG_PRINTF(4,("verify_prep: is_verified()->%d\n", rc));
 	if (rc == VE_NOT_CHECKED) {
@@ -421,23 +504,26 @@ int
 verify_file(int fd, const char *filename, off_t off, int severity,
     const char *caller)
 {
-	static int once;
+	static int check_verbose = 1;
 	static int accept_no_fp = ACCEPT_NO_FP_DEFAULT;
-	static int verbose = VE_VERBOSE_DEFAULT;
 	struct stat st;
 	char *cp;
 	int rc;
 
+	if (check_verbose) {
+		check_verbose = 0;
+		Verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT);
+		VerifyFlags = getenv_int("VE_VERIFY_FLAGS", VEF_VERBOSE);
+#ifndef UNIT_TEST
+		ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL));
+#endif
+	}
+
 	rc = verify_prep(fd, filename, off, &st, caller);
 
 	if (!rc)
 		return (0);
 
-	if (!once) {
-		once++;
-		verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT);
-	}
-
 	if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) {
 		if (severity <= VE_GUESS)
 			severity = severity_guess(filename);
@@ -455,26 +541,16 @@ verify_file(int fd, const char *filename, off_t off, int severity,
 			filename += DestdirLen;
 		}
 #endif
-		if ((rc = verify_fd(fd, filename, off, &st)) >= 0) {
-			if (verbose || severity > VE_WANT) {
-#if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0
-				printf("%serified %s %llu,%llu\n",
-				    (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V",
-				    filename,
-				    (long long)st.st_dev, (long long)st.st_ino);
-#else
-				printf("%serified %s\n",
-				    (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V",
-				    filename);
-#endif
-			}
+		rc = verify_fd(fd, filename, off, &st);
+		verify_report(filename, severity, rc, &st);
+		if (rc >= 0) {
 			if (severity < VE_MUST) { /* not a kernel or module */
 				if ((cp = strrchr(filename, '/'))) {
 					cp++;
 					if (strncmp(cp, "loader.ve.", 10) == 0) {
 						cp += 10;
 						verify_tweak(fd, off, &st, cp,
-						    &accept_no_fp, &verbose);
+						    &accept_no_fp);
 					}
 				}
 			}
@@ -482,15 +558,17 @@ verify_file(int fd, const char *filename, off_t off, int severity,
 			ve_status_set(fd, rc);
 			return (rc);
 		}
-
-		if (severity || verbose || rc == VE_FINGERPRINT_WRONG)
-			printf("Unverified: %s\n", ve_error_get());
 		if (rc == VE_FINGERPRINT_UNKNOWN && severity < VE_MUST)
 			rc = VE_UNVERIFIED_OK;
 		else if (rc == VE_FINGERPRINT_NONE && severity < accept_no_fp)
 			rc = VE_UNVERIFIED_OK;
 
 		add_verify_status(&st, rc);
+
+		/* recheck debug/verbose level next time we are called */
+		if (rc == VE_UNVERIFIED_OK) {
+			check_verbose = 1;
+		}
 	}
 #ifdef LOADER_VERIEXEC_TESTING
 	else if (rc != VE_FINGERPRINT_WRONG) {
@@ -550,7 +628,7 @@ verify_pcr_export(void)
 					hlen += KENV_MVALLEN -
 					    (hlen % KENV_MVALLEN);
 					if (snprintf(mvallen, sizeof(mvallen),
-						"%d", (int) hlen) < sizeof(mvallen))
+						"%d", (int) hlen) < (int)sizeof(mvallen))
 						setenv("kenv_mvallen", mvallen, 1);
 				}
 				free(hinfo);
@@ -559,3 +637,37 @@ verify_pcr_export(void)
 	}
 #endif
 }
+
+/*
+ * For tftp and http we need to hash pathname
+ * to be able to fake stat(2) data.
+ */
+int
+hash_string(char *s, size_t n, char *buf, size_t bufsz)
+{
+	br_hash_compat_context mctx;
+	const br_hash_class *md;
+
+	switch (bufsz) {
+	case br_sha1_SIZE:
+		md = &br_sha1_vtable;
+		break;
+	case br_sha256_SIZE:
+		md = &br_sha256_vtable;
+		break;
+	default:
+		if (bufsz < br_sha1_SIZE)
+			return -1;
+		md = &br_sha1_vtable;
+		bufsz = br_sha1_SIZE;
+		break;
+	}
+	if (n == 0)
+		n = strlen(s);
+	md->init(&mctx.vtable);
+	md->update(&mctx.vtable, s, n);
+	md->out(&mctx.vtable, buf);
+	return bufsz;
+}
+
+
diff --git a/lib/libsecureboot/vets.c b/lib/libsecureboot/vets.c
index 3a82592ea699..dd347a0a8c13 100644
--- a/lib/libsecureboot/vets.c
+++ b/lib/libsecureboot/vets.c
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
 # define TRUST_ANCHOR_STR ta_PEM
 #endif
 
+#define EPOCH_YEAR		1970
+#define AVG_SECONDS_PER_YEAR	31556952L
 #define SECONDS_PER_DAY		86400
 #define SECONDS_PER_YEAR	365 * SECONDS_PER_DAY
 #ifndef VE_UTC_MAX_JUMP
@@ -52,6 +54,11 @@ __FBSDID("$FreeBSD$");
 
 int DebugVe = 0;
 
+#ifndef VE_VERIFY_FLAGS
+# define VE_VERIFY_FLAGS VEF_VERBOSE
+#endif
+int VerifyFlags = VE_VERIFY_FLAGS;
+
 typedef VECTOR(br_x509_certificate) cert_list;
 typedef VECTOR(hash_data) digest_list;
 
@@ -109,8 +116,59 @@ ve_error_set(const char *fmt, ...)
 	return (rc);
 }
 
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+ * The *approximate* date.
+ *
+ * When certificate verification fails for being
+ * expired or not yet valid, it helps to indicate
+ * our current date.
+ * Since libsa lacks strftime and gmtime,
+ * this simple implementation suffices.
+ */
+static const char *
+gdate(char *buf, size_t bufsz, time_t clock)
+{
+	int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	int year, y, m, d;
+
+	y = clock / AVG_SECONDS_PER_YEAR;
+	year = EPOCH_YEAR + y;
+	for (y = EPOCH_YEAR; y < year; y++) {
+		clock -= SECONDS_PER_YEAR;
+		if (isleap(y))
+			clock -= SECONDS_PER_DAY;
+	}
+	d = clock / SECONDS_PER_DAY;
+	for (m = 0; d > 1 && m < 12; m++) {
+		if (d > days[m]) {
+			d -= days[m];
+			if (m == 1 && d > 0 && isleap(year))
+				d--;
+		} else
+			break;
+	}
+	d++;
+	if (d > days[m]) {
+	    d = 1;
+	    m++;
+	    if (m >= 12) {
+		year++;
+		m = 0;
+	    }
+	}
+	(void)snprintf(buf, bufsz, "%04d-%02d-%02d", year, m+1, d);
+	return(buf);
+}
+
 /* this is the time we use for verifying certs */
+#ifdef UNIT_TEST
+extern time_t ve_utc;
+time_t ve_utc = 0;
+#else
 static time_t ve_utc = 0;
+#endif
 
 /**
  * @brief
@@ -372,6 +430,40 @@ ve_trust_init(void)
 	return (once);
 }
 
+#ifdef HAVE_BR_X509_TIME_CHECK
+static int
+verify_time_cb(void *tctx,
+    uint32_t not_before_days, uint32_t not_before_seconds,
+    uint32_t not_after_days, uint32_t not_after_seconds)
+{
+	time_t not_before;
+	time_t not_after;
+	int rc;
+#ifdef UNIT_TEST
+	char date[12], nb_date[12], na_date[12];
+#endif
+
+	not_before = ((not_before_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_before_seconds;
+	not_after =  ((not_after_days - X509_DAYS_TO_UTC0) * SECONDS_PER_DAY) + not_after_seconds;
+	if (ve_utc < not_before)
+		rc = -1;
+	else if (ve_utc > not_after)
+		rc = 1;
+	else
+		rc = 0;
+#ifdef UNIT_TEST
+	printf("notBefore %s notAfter %s date %s rc %d\n",
+	    gdate(nb_date, sizeof(nb_date), not_before),
+	    gdate(na_date, sizeof(na_date), not_after),
+	    gdate(date, sizeof(date), ve_utc), rc);
+#endif
+#if defined(_STANDALONE)
+	rc = 0;				/* don't fail */
+#endif
+	return rc;
+}
+#endif
+
 /**
  * if we can verify the certificate chain in "certs",
  * return the public key and if "xcp" is !NULL the associated
@@ -425,14 +517,17 @@ verify_signer_xcs(br_x509_certificate *xcs,
 #endif
 	br_x509_minimal_set_name_elements(&mc, elts, num_elts);
 
-#ifdef _STANDALONE
+#ifdef HAVE_BR_X509_TIME_CHECK
+	br_x509_minimal_set_time_callback(&mc, NULL, verify_time_cb);
+#else
+#if defined(_STANDALONE) || defined(UNIT_TEST)
 	/*
 	 * Clock is probably bogus so we use ve_utc.
 	 */
 	mc.days = (ve_utc / SECONDS_PER_DAY) + X509_DAYS_TO_UTC0;
 	mc.seconds = (ve_utc % SECONDS_PER_DAY);
 #endif
-
+#endif
 	mc.vtable->start_chain(&mc.vtable, NULL);
 	for (u = 0; u < VEC_LEN(chain); u ++) {
 		xc = &VEC_ELT(chain, u);
@@ -452,7 +547,17 @@ verify_signer_xcs(br_x509_certificate *xcs,
 	err = mc.vtable->end_chain(&mc.vtable);
 	pk = NULL;
 	if (err) {
-		ve_error_set("Validation failed, err = %d", err);
+		char date[12];
+
+		switch (err) {
+		case 54:
+			ve_error_set("Validation failed, certificate not valid as of %s",
+			    gdate(date, sizeof(date), ve_utc));
+			break;
*** 16 LINES SKIPPED ***