diff options
| author | Simon J. Gerraty <sjg@FreeBSD.org> | 2020-03-08 17:42:42 +0000 |
|---|---|---|
| committer | Simon J. Gerraty <sjg@FreeBSD.org> | 2020-03-08 17:42:42 +0000 |
| commit | afc571b1a6fb341b0e3f603d4f3a2538093e91f5 (patch) | |
| tree | a446172ecf9eb86977386f59f06ecfdd1bc7b1b2 /lib/libsecureboot | |
| parent | 601ee53858f61e7a130dc2e306202b5297626d25 (diff) | |
Notes
Diffstat (limited to 'lib/libsecureboot')
| -rw-r--r-- | lib/libsecureboot/h/libsecureboot.h | 6 | ||||
| -rw-r--r-- | lib/libsecureboot/h/verify_file.h | 24 | ||||
| -rw-r--r-- | lib/libsecureboot/tests/tvo.c | 20 | ||||
| -rw-r--r-- | lib/libsecureboot/vectx.c | 104 | ||||
| -rw-r--r-- | lib/libsecureboot/verify_file.c | 117 |
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); } } } |
