summaryrefslogtreecommitdiff
path: root/lib/libsecureboot
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2020-03-08 17:42:42 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2020-03-08 17:42:42 +0000
commitafc571b1a6fb341b0e3f603d4f3a2538093e91f5 (patch)
treea446172ecf9eb86977386f59f06ecfdd1bc7b1b2 /lib/libsecureboot
parent601ee53858f61e7a130dc2e306202b5297626d25 (diff)
Notes
Diffstat (limited to 'lib/libsecureboot')
-rw-r--r--lib/libsecureboot/h/libsecureboot.h6
-rw-r--r--lib/libsecureboot/h/verify_file.h24
-rw-r--r--lib/libsecureboot/tests/tvo.c20
-rw-r--r--lib/libsecureboot/vectx.c104
-rw-r--r--lib/libsecureboot/verify_file.c117
5 files changed, 197 insertions, 74 deletions
diff --git a/lib/libsecureboot/h/libsecureboot.h b/lib/libsecureboot/h/libsecureboot.h
index d7d72e880744..581b72b411d2 100644
--- a/lib/libsecureboot/h/libsecureboot.h
+++ b/lib/libsecureboot/h/libsecureboot.h
@@ -69,12 +69,6 @@ void fingerprint_info_add(const char *, const char *, const char *,
int ve_check_hash(br_hash_compat_context *, const br_hash_class *,
const char *, const char *, size_t);
-struct vectx;
-struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *);
-ssize_t vectx_read(struct vectx *, void *, size_t);
-off_t vectx_lseek(struct vectx *, off_t, int);
-int vectx_close(struct vectx *);
-
char * hexdigest(char *, size_t, unsigned char *, size_t);
int verify_fd(int, const char *, off_t, struct stat *);
int verify_open(const char *, int);
diff --git a/lib/libsecureboot/h/verify_file.h b/lib/libsecureboot/h/verify_file.h
index c10f17af1469..844b8266f42c 100644
--- a/lib/libsecureboot/h/verify_file.h
+++ b/lib/libsecureboot/h/verify_file.h
@@ -39,13 +39,21 @@
struct stat;
-void ve_debug_set(int);
-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);
+int verify_prep(int, const char *, off_t, struct stat *, const char *);
+void ve_debug_set(int);
+char *ve_error_get(void);
+void ve_efi_init(void);
+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);
+int verify_file(int, const char *, off_t, int, const char *);
+void verify_pcr_export(void);
+
+struct vectx;
+struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *, const char *);
+ssize_t vectx_read(struct vectx *, void *, size_t);
+off_t vectx_lseek(struct vectx *, off_t, int);
+int vectx_close(struct vectx *, int, const char *);
#endif /* _VERIFY_FILE_H_ */
diff --git a/lib/libsecureboot/tests/tvo.c b/lib/libsecureboot/tests/tvo.c
index ad3bccb0614f..879d87f128f1 100644
--- a/lib/libsecureboot/tests/tvo.c
+++ b/lib/libsecureboot/tests/tvo.c
@@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <verify_file.h>
+size_t DestdirLen;
+char *Destdir;
char *Skip;
int
@@ -42,7 +44,10 @@ main(int argc, char *argv[])
int Vflag;
char *cp;
char *prefix;
+ char *destdir;
+ Destdir = NULL;
+ DestdirLen = 0;
prefix = NULL;
Skip = NULL;
@@ -50,8 +55,12 @@ main(int argc, char *argv[])
printf("Trust %d\n", n);
Vflag = 0;
- while ((c = getopt(argc, argv, "dp:s:T:V")) != -1) {
+ while ((c = getopt(argc, argv, "D:dp:s:T:V")) != -1) {
switch (c) {
+ case 'D':
+ Destdir = optarg;
+ DestdirLen = strlen(optarg);
+ break;
case 'd':
DebugVe++;
break;
@@ -92,7 +101,7 @@ main(int argc, char *argv[])
*/
int x;
- x = verify_file(fd, argv[optind], 0, VE_GUESS);
+ x = verify_file(fd, argv[optind], 0, VE_GUESS, __func__);
printf("verify_file(%s) = %d\n", argv[optind], x);
close(fd);
}
@@ -147,7 +156,7 @@ main(int argc, char *argv[])
lseek(fd, 0, SEEK_SET);
off = st.st_size % 512;
vp = vectx_open(fd, argv[optind], off,
- &st, &error);
+ &st, &error, __func__);
if (!vp) {
printf("vectx_open(%s) failed: %d %s\n",
argv[optind], error,
@@ -155,7 +164,8 @@ main(int argc, char *argv[])
} else {
off = vectx_lseek(vp,
(st.st_size % 1024), SEEK_SET);
-
+ /* we can seek backwards! */
+ off = vectx_lseek(vp, off/2, SEEK_SET);
if (off < st.st_size) {
n = vectx_read(vp, buf,
sizeof(buf));
@@ -165,7 +175,7 @@ main(int argc, char *argv[])
off = vectx_lseek(vp, 0, SEEK_END);
/* repeating that should be harmless */
off = vectx_lseek(vp, 0, SEEK_END);
- error = vectx_close(vp);
+ error = vectx_close(vp, VE_MUST, __func__);
if (error) {
printf("vectx_close(%s) == %d %s\n",
argv[optind], error,
diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c
index 97a8d97efb88..908e24fb554c 100644
--- a/lib/libsecureboot/vectx.c
+++ b/lib/libsecureboot/vectx.c
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#endif
#include "libsecureboot-priv.h"
+#include <verify_file.h>
/**
* @file vectx.c
@@ -50,12 +51,14 @@ struct vectx {
const char *vec_path; /* path we are verifying */
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 */
size_t vec_hashsz; /* size of hash */
int vec_fd; /* file descriptor */
int vec_status; /* verification status */
};
+
/**
* @brief
* verify an open file as we read it
@@ -86,24 +89,31 @@ struct vectx {
* NULL is only returned for non-files or out-of-memory.
*/
struct vectx *
-vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
+vectx_open(int fd, const char *path, off_t off, struct stat *stp,
+ int *error, const char *caller)
{
struct vectx *ctx;
struct stat st;
size_t hashsz;
char *cp;
+ int rc;
- if (!stp) {
- if (fstat(fd, &st) == 0)
- stp = &st;
- }
+ if (!stp)
+ stp = &st;
- /* we *should* only get called for files */
- if (stp && !S_ISREG(stp->st_mode)) {
- *error = 0;
+ rc = verify_prep(fd, path, off, stp, __func__);
+
+ DEBUG_PRINTF(2,
+ ("vectx_open: caller=%s,name='%s',prep_rc=%d\n",
+ caller,path, rc));
+
+ switch (rc) {
+ case VE_FINGERPRINT_NONE:
+ case VE_FINGERPRINT_UNKNOWN:
+ case VE_FINGERPRINT_WRONG:
+ *error = rc;
return (NULL);
}
-
ctx = malloc(sizeof(struct vectx));
if (!ctx)
goto enomem;
@@ -111,10 +121,16 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
ctx->vec_path = path;
ctx->vec_size = stp->st_size;
ctx->vec_off = 0;
+ ctx->vec_hashed = 0;
ctx->vec_want = NULL;
ctx->vec_status = 0;
- hashsz = 0;
+ ctx->vec_hashsz = hashsz = 0;
+ if (rc == 0) {
+ /* we are not verifying this */
+ *error = 0;
+ return (ctx);
+ }
cp = fingerprint_info_lookup(fd, path);
if (!cp) {
ctx->vec_status = VE_FINGERPRINT_NONE;
@@ -161,6 +177,10 @@ vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
vectx_lseek(ctx, off, SEEK_SET);
}
}
+ DEBUG_PRINTF(2,
+ ("vectx_open: caller=%s,name='%s',hashsz=%lu,status=%d\n",
+ caller, path, (unsigned long)ctx->vec_hashsz,
+ ctx->vec_status));
return (ctx);
enomem: /* unlikely */
@@ -175,6 +195,8 @@ enomem: /* unlikely */
*
* It is critical that all file I/O comes through here.
* We keep track of current offset.
+ * We also track what offset we have hashed to,
+ * so we won't replay data if we seek backwards.
*
* @param[in] pctx
* pointer to ctx
@@ -190,6 +212,8 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
{
unsigned char *bp = buf;
int n;
+ int delta;
+ int x;
size_t off;
if (ctx->vec_hashsz == 0) /* nothing to do */
@@ -201,9 +225,20 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
if (n < 0)
return (n);
if (n > 0) {
- ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
- off += n;
- ctx->vec_off += n;
+ /* we may have seeked backwards! */
+ delta = ctx->vec_hashed - ctx->vec_off;
+ if (delta > 0) {
+ x = MIN(delta, n);
+ off += x;
+ n -= x;
+ ctx->vec_off += x;
+ }
+ if (n > 0) {
+ ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
+ off += n;
+ ctx->vec_off += n;
+ ctx->vec_hashed += n;
+ }
}
} while (n > 0 && off < nbytes);
return (off);
@@ -213,10 +248,10 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
* @brief
* vectx equivalent of lseek
*
- * We do not actually, seek, but call vectx_read
+ * When seeking forwards we actually call vectx_read
* to reach the desired offset.
*
- * We do not support seeking backwards.
+ * We support seeking backwards.
*
* @param[in] pctx
* pointer to ctx
@@ -225,6 +260,8 @@ vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
* desired offset
*
* @param[in] whence
+ * We try to convert whence to ``SEEK_SET``.
+ * We do not support ``SEEK_DATA`` or ``SEEK_HOLE``.
*
* @return offset or error.
*/
@@ -239,22 +276,26 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence)
return (lseek(ctx->vec_fd, off, whence));
/*
- * Try to convert whence to SEEK_SET
- * but we cannot support seeking backwards!
- * Nor beyond end of file.
+ * Convert whence to SEEK_SET
*/
if (whence == SEEK_END && off <= 0) {
whence = SEEK_SET;
off += ctx->vec_size;
- } else if (whence == SEEK_CUR && off >= 0) {
+ } else if (whence == SEEK_CUR) {
whence = SEEK_SET;
off += ctx->vec_off;
}
- if (whence != SEEK_SET || off < ctx->vec_off ||
+ if (whence != SEEK_SET ||
(size_t)off > ctx->vec_size) {
- printf("ERROR: %s: unsupported operation\n", __func__);
+ printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n",
+ __func__, whence, (long long)ctx->vec_off, (long long)off);
return (-1);
}
+ if (off < ctx->vec_hashed) {
+ /* seeking backwards! just do it */
+ ctx->vec_off = lseek(ctx->vec_fd, off, whence);
+ return (ctx->vec_off);
+ }
n = 0;
do {
delta = off - ctx->vec_off;
@@ -281,16 +322,35 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence)
* @return 0 or an error.
*/
int
-vectx_close(struct vectx *ctx)
+vectx_close(struct vectx *ctx, int severity, const char *caller)
{
int rc;
if (ctx->vec_hashsz == 0) {
rc = ctx->vec_status;
} else {
+#ifdef VE_PCR_SUPPORT
+ /*
+ * Only update pcr with things that must verify
+ * these tend to be processed in a more deterministic
+ * order, which makes our pseudo pcr more useful.
+ */
+ ve_pcr_updating_set((severity == VE_MUST));
+#endif
rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
}
+ DEBUG_PRINTF(2,
+ ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n",
+ caller,ctx->vec_path, rc, severity));
+ if (severity > VE_WANT || rc == VE_FINGERPRINT_WRONG)
+ printf("%serified %s\n", (rc <= 0) ? "Unv" : "V",
+ ctx->vec_path);
+#if !defined(UNIT_TEST) && !defined(DEBUG_VECTX)
+ /* we are generally called with VE_MUST */
+ if (severity > VE_WANT && rc == VE_FINGERPRINT_WRONG)
+ panic("cannot continue");
+#endif
free(ctx);
return ((rc < 0) ? rc : 0);
}
diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c
index 92845fb6879d..eee749667759 100644
--- a/lib/libsecureboot/verify_file.c
+++ b/lib/libsecureboot/verify_file.c
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
* define MANIFEST_SKIP to Skip - in tests/tvo.c so that
* tvo can control the value we use in find_manifest()
*/
+extern char *Destdir;
+extern size_t DestdirLen;
extern char *Skip;
# undef MANIFEST_SKIP
# define MANIFEST_SKIP Skip
@@ -167,12 +169,21 @@ load_manifest(const char *name, const char *prefix,
ve_utc_set(stp->st_mtime);
content = (char *)verify_signed(name, VEF_VERBOSE);
if (content) {
+#ifdef UNIT_TEST
+ if (DestdirLen > 0 &&
+ strncmp(name, Destdir, DestdirLen) == 0) {
+ name += DestdirLen;
+ if (prefix &&
+ strncmp(prefix, Destdir, DestdirLen) == 0)
+ prefix += DestdirLen;
+ }
+#endif
fingerprint_info_add(name, prefix, skip, content, stp);
add_verify_status(stp, VE_VERIFIED);
loaded_manifests = 1; /* we are verifying! */
DEBUG_PRINTF(3, ("loaded: %s %s %s\n",
name, prefix, skip));
- rc = 0;
+ rc = VE_VERIFIED;
} else {
rc = VE_FINGERPRINT_WRONG;
add_verify_status(stp, rc); /* remember */
@@ -245,13 +256,15 @@ severity_guess(const char *filename)
return (VE_WANT);
}
+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, int *verifying)
+ int *verbose)
{
if (strcmp(tweak, "off") == 0) {
- *verifying = 0;
+ Verifying = 0;
} else if (strcmp(tweak, "strict") == 0) {
/* anything caller wants verified must be */
*accept_no_fp = VE_WANT;
@@ -314,6 +327,58 @@ getenv_int(const char *var, int def)
return (int)val;
}
+
+/**
+ * @brief prepare to verify an open file
+ *
+ * @param[in] fd
+ * open descriptor
+ *
+ * @param[in] filename
+ * path we opened and will use to lookup fingerprint
+ *
+ * @param[in] stp
+ * stat pointer so we can check file type
+ */
+int
+verify_prep(int fd, const char *filename, off_t off, struct stat *stp,
+ const char *caller)
+{
+ int rc;
+
+ 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);
+ ve_status_state = VE_STATUS_NONE;
+ if (Verifying) {
+ ve_self_tests();
+ ve_anchor_verbose_set(1);
+ }
+ }
+ if (!Verifying || fd < 0)
+ return (0);
+ if (stp) {
+ if (fstat(fd, stp) < 0 || !S_ISREG(stp->st_mode))
+ return (0);
+ }
+ DEBUG_PRINTF(2,
+ ("caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n",
+ caller, fd, filename, (long long)off, (long long)stp->st_dev,
+ (long long)stp->st_ino));
+ rc = is_verified(stp);
+ if (rc == VE_NOT_CHECKED) {
+ rc = find_manifest(filename);
+ } else {
+ ve_status_set(fd, rc);
+ }
+ return (rc);
+}
+
/**
* @brief verify an open file
*
@@ -342,45 +407,26 @@ getenv_int(const char *var, int def)
* @return >= 0 on success < 0 on failure
*/
int
-verify_file(int fd, const char *filename, off_t off, int severity)
+verify_file(int fd, const char *filename, off_t off, int severity,
+ const char *caller)
{
- static int verifying = -1;
+ static int once;
static int accept_no_fp = ACCEPT_NO_FP_DEFAULT;
static int verbose = VE_VERBOSE_DEFAULT;
struct stat st;
char *cp;
int rc;
- if (verifying < 0) {
- verifying = ve_trust_init();
- verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT);
- ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL));
- /* initialize ve_status with default result */
- rc = verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING;
- ve_status_set(0, rc);
- ve_status_state = VE_STATUS_NONE;
- if (verifying) {
- ve_self_tests();
- ve_anchor_verbose_set(1);
- }
- }
- if (!verifying)
- return (0);
+ rc = verify_prep(fd, filename, off, &st, caller);
- if (fd < 0 || fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+ if (!rc)
return (0);
- DEBUG_PRINTF(3, ("fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n",
- fd, filename, (long long)off, (long long)st.st_dev,
- (long long)st.st_ino));
-
-
- rc = is_verified(&st);
- if (rc != VE_NOT_CHECKED) {
- ve_status_set(fd, rc);
- return (rc);
+ if (!once) {
+ once++;
+ verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT);
}
- rc = find_manifest(filename);
+
if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) {
if (severity <= VE_GUESS)
severity = severity_guess(filename);
@@ -392,6 +438,12 @@ verify_file(int fd, const char *filename, off_t off, int severity)
*/
ve_pcr_updating_set((severity == VE_MUST));
#endif
+#ifdef UNIT_TEST
+ if (DestdirLen > 0 &&
+ strncmp(filename, Destdir, DestdirLen) == 0) {
+ 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
@@ -412,8 +464,7 @@ verify_file(int fd, const char *filename, off_t off, int severity)
if (strncmp(cp, "loader.ve.", 10) == 0) {
cp += 10;
verify_tweak(fd, off, &st, cp,
- &accept_no_fp, &verbose,
- &verifying);
+ &accept_no_fp, &verbose);
}
}
}