From nobody Mon Apr 18 19:54:22 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id B86F211E6604; Mon, 18 Apr 2022 19:54:23 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4KhyLk5T2fz4k5L; Mon, 18 Apr 2022 19:54:22 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1650311662; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qj27VHBhNATvoOIw+kQcxfev8vao0ET1xJAcUUdsCoY=; b=oXCpYVM/hv4Xhx1MOj/vnQEILU1HvZt+pFpU/uDk6gTe2A+LJqHoDAnHc66rhX1odHNOiQ 5rn9F0swR6sunmzBHEHAcD++NvhS36u9zTZ9Ux+5eOxbxgsHIZJdhPT4DeBnY0wtS/s6XM 6oHcOaum/mQg4vC9wI1K6dldiIl/b+pWcL4GfjphBYRkjpNwgHo26rbQ1m/TWl+klBD2+K dlDyaOQykgEVizCk5h2t23ZN3Xovc4Qj2T7c/wfbfJK99b2eIEQSfDcjAmmjyLQl80pXYT JMetUT9SY6H8F7k8kM/htaZ5X6hkSh9UlIF0k39jDrjkOUP/k6ibjM2EYu2iXA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 7E3E4194F5; Mon, 18 Apr 2022 19:54:22 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 23IJsM4w056347; Mon, 18 Apr 2022 19:54:22 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 23IJsM9f056346; Mon, 18 Apr 2022 19:54:22 GMT (envelope-from git) Date: Mon, 18 Apr 2022 19:54:22 GMT Message-Id: <202204181954.23IJsM9f056346@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Simon J. Gerraty" Subject: git: 666554111a7e - main - Update libsecureboot List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: sjg X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 666554111a7e6b4c1a9a6ff2e73f12cd582573bb Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1650311662; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=qj27VHBhNATvoOIw+kQcxfev8vao0ET1xJAcUUdsCoY=; b=Ss+JY2ShVw9t/vUIVGIzt73yJypwW8JmQTUYOi4DI6qiAGL1b1G9guAoxThvzQRSetXMm4 udkgz6KCBPLpqhVHXhkNrWxXXCMRJ6Jz5eEWthsHjDKcbnifDErpps0Qs5dmRDl/22Lnzf 8qsdGP2b10hZw2hIOTZTk8Ngm7U9D++bmlHnPNyWWMsnma2QmHQMLSEN28b/mGGthCpQHB wqWbPYwhOY3MAG0REsEy8L48qXvjuM/3yBOFKR4FzAdjH8t/1Qci55qnTdTj3hkI2mT38w ub1CXC/fyKp4n+3MsXeix/iJxmH6e7XsDI9K+lJ+3r60luEzz/d2RFR7eX+LZg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1650311662; a=rsa-sha256; cv=none; b=pc9Vw3Mz0FcZYndrkQB0CEnplJvCaGSihaiLdASuowgdy9dzGg6Kpj662fdtohy+m+az8x EKC0IA+sQ110hhMO6TRrf8Sbxadu7zGz7DKZiOiCitmWq6M8YnDTm+xQdXq61dJfTEK+Ci turn24ApDRqhRxhPx54jMJp27ygqpDW4li3NWYldkRrEEyAIemKgsFGTGfy7BNQBehk3AT xx5E5vA53qw+nVB0VyoE5Psvo85vUOtJr3tZHsk/K4xFlwKTYgYWV/yJh79d6RSR0x17M3 hmmMDybR8e/9S49dzoVyMe6RqQxiyaN2RIV5nMVDTttvkK7QfmroCSKuMEQW1Q== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by sjg: URL: https://cgit.FreeBSD.org/src/commit/?id=666554111a7e6b4c1a9a6ff2e73f12cd582573bb commit 666554111a7e6b4c1a9a6ff2e73f12cd582573bb Author: Simon J. Gerraty AuthorDate: 2022-04-18 19:53:53 +0000 Commit: Simon J. Gerraty 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 #include +/* 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 @@ -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 ***