aboutsummaryrefslogtreecommitdiff
path: root/lib/libsecureboot
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2022-04-18 19:53:53 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2022-04-18 19:54:15 +0000
commit666554111a7e6b4c1a9a6ff2e73f12cd582573bb (patch)
tree53bbb3445636453e1e1a31cb9935d75445531db6 /lib/libsecureboot
parent43d5661a9d0f44c73344e0f2fc094b7286987313 (diff)
downloadsrc-666554111a7e6b4c1a9a6ff2e73f12cd582573bb.tar.gz
src-666554111a7e6b4c1a9a6ff2e73f12cd582573bb.zip
Diffstat (limited to 'lib/libsecureboot')
-rw-r--r--lib/libsecureboot/h/libsecureboot.h3
-rw-r--r--lib/libsecureboot/h/verify_file.h11
-rw-r--r--lib/libsecureboot/openpgp/opgp_sig.c4
-rw-r--r--lib/libsecureboot/readfile.c38
-rw-r--r--lib/libsecureboot/tests/tvo.c45
-rw-r--r--lib/libsecureboot/vectx.c50
-rw-r--r--lib/libsecureboot/veopen.c13
-rw-r--r--lib/libsecureboot/verify_file.c208
-rw-r--r--lib/libsecureboot/vets.c113
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;
@@ -339,6 +363,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
*
* @param[in] fd
@@ -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;
+ default:
+ ve_error_set("Validation failed, err = %d", err);
+ break;
+ }
} else {
tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
if (tpk != NULL) {
@@ -866,7 +971,7 @@ verify_sig(const char *sigfile, int flags)
if (!ucp) {
printf("Unverified %s (%s)\n", pbuf,
cn.status ? cn_buf : "unknown");
- } else if ((flags & 1) != 0) {
+ } else if ((flags & VEF_VERBOSE) != 0) {
printf("Verified %s signed by %s\n", pbuf,
cn.status ? cn_buf : "someone we trust");
}