From f616d61ab6b071e5fbfdbae7033a9ef04c1444ad Mon Sep 17 00:00:00 2001 From: "Simon J. Gerraty" Date: Mon, 12 Feb 2024 14:35:01 -0800 Subject: [PATCH] libsecureboot do not report expected unverified files By default only report unverified files at severity VE_WANT and above. This inlcudes *.conf but not *.hints, *.cookie or *.tgz which get VE_TRY as their severity. If Verbose is set to 0, then VerifyFlags should default to 0 too. Thus the combination of module_verbose=0 VE_VEBOSE=0 is sufficient to make the loader almost totally silent. When verify_prep has to find_manifest and it is verified ok return VE_NOT_CHECKED to verify_file so that it can skip repeating verify_fd Also add better debugging output for is_verified and add_verify_status. vectx handle compressed modules When verifying a compressed module (.ko.gz or .ko.bz2) stat() reports the size as -1 (unknown). vectx_lseek needs to spot this during closing - and just read until EOF is hit. Note: because of the way libsa's open() works, verify_prep will see the path to be verified as module.ko not module.ko.bz2 etc. This is actually ok, because we need a separate module.ko.bz2 entry so that the package can be verified, and the hash for module.ko is of the uncompressed file which is what vectx will see. Re-work local.trust.mk so site.trust.mk need only set VE_SIGN_URL_LIST (if using the mentioned signing server) interp.c: restrict interactive input Apply the same restrictions to interactive input as for unverified conf and hints files. Use version.veriexec when LOADER_VERIEXEC is yes Reviewed by: kevans Sponsored by: Juniper Networks, Inc. Differential Revision: https://reviews.freebsd.org/D43810 --- lib/libsecureboot/Makefile.inc | 8 +- lib/libsecureboot/Makefile.libsa.inc | 7 +- lib/libsecureboot/h/verify_file.h | 1 + lib/libsecureboot/local.trust.mk | 147 ++++++++++++--------------- lib/libsecureboot/vectx.c | 26 +++-- lib/libsecureboot/verify_file.c | 35 +++++-- stand/common/interp.c | 8 ++ stand/efi/loader/version.veriexec | 7 ++ stand/veriexec.mk | 3 + 9 files changed, 138 insertions(+), 104 deletions(-) create mode 100644 stand/efi/loader/version.veriexec diff --git a/lib/libsecureboot/Makefile.inc b/lib/libsecureboot/Makefile.inc index ff40b919bad3..b09e44edc1b0 100644 --- a/lib/libsecureboot/Makefile.inc +++ b/lib/libsecureboot/Makefile.inc @@ -58,6 +58,10 @@ _2ndLAST_PEM_USE: .USE sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \ sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET} +# rules to populate the [tv]*.pem files we use to generate ta.h +# and can add/alter VE_*_LIST as desired. +.-include "local.trust.mk" + # list of hashes we support VE_HASH_LIST?= SHA256 @@ -74,10 +78,6 @@ VE_SIGNATURE_EXT_LIST?= sig # needs to be yes for FIPS 140-2 compliance VE_SELF_TESTS?= no -# rules to populate the [tv]*.pem files we use to generate ta.h -# and can add/alter VE_*_LIST as desired. -.-include "local.trust.mk" - # this is what we use as our trust anchor CFLAGS+= -I. -DTRUST_ANCHOR_STR=ta_PEM diff --git a/lib/libsecureboot/Makefile.libsa.inc b/lib/libsecureboot/Makefile.libsa.inc index 76e0a91bc20a..907c8e8f7533 100644 --- a/lib/libsecureboot/Makefile.libsa.inc +++ b/lib/libsecureboot/Makefile.libsa.inc @@ -46,9 +46,12 @@ manifests.h: echo '${VE_MANIFEST_LIST:@m@"$m",${.newline}@}'; \ echo 'NULL };' ) > ${.TARGET} +# only add these if set XCFLAGS.verify_file+= \ - -DVE_DEBUG_LEVEL=${VE_DEBUG_LEVEL:U0} \ - -DVE_VERBOSE_DEFAULT=${VE_VERBOSE_DEFAULT:U0} \ + ${VE_DEBUG_LEVEL \ + VE_VERBOSE_DEFAULT \ + VE_VERIFY_FLAGS \ + :L:@v@${$v:S,^,-D$v=,}@} .if !empty(MANIFEST_SKIP_ALWAYS) XCFLAGS.verify_file+= -DMANIFEST_SKIP_ALWAYS=\"${MANIFEST_SKIP_ALWAYS}\" diff --git a/lib/libsecureboot/h/verify_file.h b/lib/libsecureboot/h/verify_file.h index 88d758b27af4..f918ed6d0e38 100644 --- a/lib/libsecureboot/h/verify_file.h +++ b/lib/libsecureboot/h/verify_file.h @@ -46,6 +46,7 @@ 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); +void ve_status_set(int, int); int ve_status_get(int); int load_manifest(const char *, const char *, const char *, struct stat *); int pass_manifest(const char *, const char *); diff --git a/lib/libsecureboot/local.trust.mk b/lib/libsecureboot/local.trust.mk index 7b1e5f7ee97b..b009139a2f68 100644 --- a/lib/libsecureboot/local.trust.mk +++ b/lib/libsecureboot/local.trust.mk @@ -5,65 +5,69 @@ # the signing server (http://www.crufty.net/sjg/blog/signing-server.htm) # for each key will provide the appropriate certificate chain on request -# force these for Junos -#MANIFEST_SKIP_ALWAYS= boot -VE_HASH_LIST= \ - SHA1 \ - SHA256 \ - SHA384 \ - SHA512 - -VE_SIGNATURE_LIST= \ - ECDSA \ - RSA - -VE_SIGNATURE_EXT_LIST= \ - esig \ - rsig - -VE_SELF_TESTS= yes - -.if ${MACHINE} == "host" && ${.CURDIR:T} == "tests" - -VE_SIGNATURE_LIST+= \ - DEPRECATED_RSA_SHA1 - -VE_SIGNATURE_EXT_LIST+= \ - sig -.endif - -# add OpenPGP support - possibly dormant -VE_SIGNATURE_LIST+= OPENPGP -VE_SIGNATURE_EXT_LIST+= asc - -# allow site override of all the above +# allow site control .-include "site.trust.mk" -SIGNER ?= ${SB_TOOLS_PATH:U/volume/buildtools/bin}/sign.py +#VE_DEBUG_LEVEL?=3 +#VE_VERBOSE_DEFAULT?=2 + +VE_HASH_LIST?= \ + SHA256 \ + SHA384 \ + +VE_SELF_TESTS?= yes + +# client for the signing server above +SIGNER?= /opt/sigs/sign.py .if exists(${SIGNER}) -SIGN_HOST ?= ${SB_SITE:Usvl}-junos-signer.juniper.net -ECDSA_PORT:= ${133%y:L:gmtime} -SIGN_ECDSA= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${ECDSA_PORT} -h sha256 -RSA2_PORT:= ${163%y:L:gmtime} -SIGN_RSA2= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${RSA2_PORT} -h sha256 +OPENPGP_SIGNER?= ${SIGNER:H}/openpgp-sign.py +OPENPGP_SIGN_FLAGS= -a +OPENPGP_SIGN_HOST?= localhost +SIGN_HOST ?= localhost -# deal with quirk of our .esig format -XCFLAGS.vets+= -DVE_ECDSA_HASH_AGAIN +# A list of name/ext/url tuples. +# name should be one of ECDSA, OPENPGP or RSA, they can be repeated +# Order of ext list implies runtime preference so do not sort! +VE_SIGN_URL_LIST?= \ + ECDSA/esig/${SIGN_HOST}:${133%y:L:localtime} \ + RSA/rsig/${SIGN_HOST}:${163%y:L:localtime} \ + OPENPGP/asc/${OPENPGP_SIGN_HOST}:1234 \ -.if !empty(OPENPGP_SIGN_URL) +.for sig ext url in ${VE_SIGN_URL_LIST:@x@${x:H:H} ${x:H:T} ${x:T}@} +SIGN_${sig}:= ${PYTHON} ${${sig}_SIGNER:U${SIGNER}} -u ${url} ${${sig}_SIGN_FLAGS:U-h sha256} + +VE_SIGNATURE_LIST+= ${sig} +VE_SIGNATURE_EXT_LIST+= ${ext} + +_SIGN_${sig}_USE: .USE + ${SIGN_${sig}} ${.ALLSRC} + +_TA_${sig}_USE: .USE + ${SIGN_${sig}} -C ${.TARGET} + +.if ${sig} == "OPENPGP" +ta_${sig:tl}.${ext}: _TA_${sig}_USE +ta_${ext}.h: ta_${sig:tl}.${ext} +.else +${ext:S/sig/certs/}.pem: _TA_${sig}_USE +# the last cert in the chain is the one we want +ta_${ext}.pem: ${ext:S/sig/certs/}.pem _LAST_PEM_USE +ta.h: ta_${ext}.pem +.if ${VE_SELF_TESTS} != "no" +# we use the 2nd last cert to test verification +vc_${ext}.pem: ${ext:S/sig/certs/}.pem _2ndLAST_PEM_USE +ta.h: vc_${ext}.pem +.endif +.endif +.endfor + +# cleanup duplicates +VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:O:u} + +.if target(ta_asc.h) XCFLAGS.opgp_key+= -DHAVE_TA_ASC_H -VE_SIGNATURE_LIST+= OPENPGP -VE_SIGNATURE_EXT_LIST+= asc - -SIGN_OPENPGP= ${PYTHON} ${SIGNER:H}/openpgp-sign.py -a -u ${OPENPGP_SIGN_URL} - -ta_openpgp.asc: - ${SIGN_OPENPGP} -C ${.TARGET} - -ta_asc.h: ta_openpgp.asc - .if ${VE_SELF_TESTS} != "no" # for self test vc_openpgp.asc: ta_openpgp.asc @@ -74,48 +78,26 @@ ta_asc.h: vc_openpgp.asc .endif .endif -rcerts.pem: - ${SIGN_RSA2} -C ${.TARGET} - -ecerts.pem: - ${SIGN_ECDSA} -C ${.TARGET} - -.if ${VE_SIGNATURE_LIST:tu:MECDSA} != "" -# the last cert in the chain is the one we want -ta_ec.pem: ecerts.pem _LAST_PEM_USE -ta.h: ta_ec.pem -.if ${VE_SELF_TESTS} != "no" -# these are for verification self test -vc_ec.pem: ecerts.pem _2ndLAST_PEM_USE -ta.h: vc_ec.pem -.endif -.endif - -.if ${VE_SIGNATURE_LIST:tu:MRSA} != "" -ta_rsa.pem: rcerts.pem _LAST_PEM_USE -ta.h: ta_rsa.pem -.if ${VE_SELF_TESTS} != "no" -vc_rsa.pem: rcerts.pem _2ndLAST_PEM_USE -ta.h: vc_rsa.pem -.endif -.endif - -# we take the mtime of this as our baseline time -#BUILD_UTC_FILE= ecerts.pem -#VE_DEBUG_LEVEL=3 -#VE_VERBOSE_DEFAULT=1 - .else +VE_SIGNATURE_LIST?= RSA + # you need to provide t*.pem or t*.asc files for each trust anchor +# below assumes they are named ta_${ext}.pem eg ta_esig.pem for ECDSA .if empty(TRUST_ANCHORS) TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null .endif .if empty(TRUST_ANCHORS) && ${MK_LOADER_EFI_SECUREBOOT} != "yes" .error Need TRUST_ANCHORS see ${.PARSEDIR}/README.rst .endif + .if ${TRUST_ANCHORS:T:Mt*.pem} != "" ta.h: ${TRUST_ANCHORS:M*.pem} +VE_SIGNATURE_EXT_LIST?= ${TRUST_ANCHORS:T:Mt*.pem:R:S/ta_//} +.if ${VE_SIGNATURE_EXT_LIST:Mesig} != "" +VE_SIGNATURE_LIST+= ECDSA .endif +.endif + .if ${TRUST_ANCHORS:T:Mt*.asc} != "" VE_SIGNATURE_LIST+= OPENPGP VE_SIGNATURE_EXT_LIST+= asc @@ -124,4 +106,3 @@ ta_asc.h: ${TRUST_ANCHORS:M*.asc} # we take the mtime of this as our baseline time BUILD_UTC_FILE?= ${TRUST_ANCHORS:[1]} .endif - diff --git a/lib/libsecureboot/vectx.c b/lib/libsecureboot/vectx.c index dba728421ce4..2d56830cd81d 100644 --- a/lib/libsecureboot/vectx.c +++ b/lib/libsecureboot/vectx.c @@ -306,19 +306,31 @@ vectx_lseek(struct vectx *ctx, off_t off, int whence) 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))); + if (ctx->vec_size < 0) { + if (ctx->vec_closing) { + /* size unknown - read until EOF */ + do { + n = vectx_read(ctx, buf, PAGE_SIZE); + if (n < 0) + return (n); + } while (n > 0); + return (ctx->vec_off); + } + } else { + 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; } - whence = SEEK_SET; - off += ctx->vec_size; } else if (whence == SEEK_CUR) { whence = SEEK_SET; off += ctx->vec_off; } if (whence != SEEK_SET || - off > ctx->vec_size) { + (off > ctx->vec_size && ctx->vec_size > 0)) { printf("ERROR: %s: unsupported operation: whence=%d off=%ld -> %ld\n", __func__, whence, (long)ctx->vec_off, (long)off); return (-1); diff --git a/lib/libsecureboot/verify_file.c b/lib/libsecureboot/verify_file.c index c123ea9e1088..753204a33b6a 100644 --- a/lib/libsecureboot/verify_file.c +++ b/lib/libsecureboot/verify_file.c @@ -82,7 +82,7 @@ static int Verbose = VE_VERBOSE_DEFAULT; /** * @brief set ve status for fd */ -static void +void ve_status_set(int fd, int ves) { if (fd >= 0 && fd < SOPEN_MAX) { @@ -131,15 +131,21 @@ int is_verified(struct stat *stp) { struct verify_status *vsp; + int rc = VE_NOT_CHECKED; if (stp->st_ino > 0) { for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) { if (stp->st_dev == vsp->vs_dev && - stp->st_ino == vsp->vs_ino) - return (vsp->vs_status); + stp->st_ino == vsp->vs_ino) { + rc = vsp->vs_status; + break; + } } } - return (VE_NOT_CHECKED); + DEBUG_PRINTF(4, ("%s: dev=%lld,ino=%llu,status=%d\n", + __func__, (long long)stp->st_dev, + (unsigned long long)stp->st_ino, rc)); + return (rc); } /* most recent first, since most likely to see repeated calls. */ @@ -156,6 +162,9 @@ add_verify_status(struct stat *stp, int status) vsp->vs_status = status; verified_files = vsp; } + DEBUG_PRINTF(4, ("%s: dev=%lld,ino=%llu,status=%d\n", + __func__, (long long)stp->st_dev, + (unsigned long long)stp->st_ino, status)); } @@ -270,11 +279,14 @@ severity_guess(const char *filename) /* * Some files like *.conf and *.hints may be unsigned, * a *.tgz is expected to have its own signed manifest. + * We allow *.conf to get VE_WANT, but files we expect + * to always be unverified get VE_TRY and we will not + * report them. */ if ((cp = strrchr(filename, '.'))) { - if (strcmp(cp, ".conf") == 0 || - strcmp(cp, ".cookie") == 0 || + if (strcmp(cp, ".cookie") == 0 || strcmp(cp, ".hints") == 0 || + strcmp(cp, ".order") == 0 || strcmp(cp, ".tgz") == 0) return (VE_TRY); if (strcmp(cp, ".4th") == 0 || @@ -398,6 +410,8 @@ void verify_report(const char *path, int severity, int status, struct stat *stp) { if (status < 0 || status == VE_FINGERPRINT_IGNORE) { + if (Verbose < VE_VERBOSE_ALL && severity < VE_WANT) + return; if (Verbose >= VE_VERBOSE_UNVERIFIED || severity > VE_TRY || status <= VE_FINGERPRINT_WRONG) { if (Verbose == VE_VERBOSE_DEBUG && stp != NULL) @@ -462,9 +476,10 @@ verify_prep(int fd, const char *filename, off_t off, struct stat *stp, caller, fd, filename, (long long)off, (long long)stp->st_dev, (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) { rc = find_manifest(filename); + if (rc == VE_VERIFIED) + rc = VE_NOT_CHECKED; } else { ve_status_set(fd, rc); } @@ -511,7 +526,8 @@ verify_file(int fd, const char *filename, off_t off, int severity, if (check_verbose) { check_verbose = 0; Verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT); - VerifyFlags = getenv_int("VE_VERIFY_FLAGS", VEF_VERBOSE); + VerifyFlags = getenv_int("VE_VERIFY_FLAGS", + Verbose ? VEF_VERBOSE : 0); #ifndef UNIT_TEST ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL)); #endif @@ -523,6 +539,9 @@ verify_file(int fd, const char *filename, off_t off, int severity, return (0); if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) { + if (rc != VE_NOT_CHECKED) + return (rc); + if (severity <= VE_GUESS) severity = severity_guess(filename); #ifdef VE_PCR_SUPPORT diff --git a/stand/common/interp.c b/stand/common/interp.c index b71e0858e702..0f142902b4ac 100644 --- a/stand/common/interp.c +++ b/stand/common/interp.c @@ -35,6 +35,10 @@ #include #include "bootstrap.h" +#ifdef LOADER_VERIEXEC +#include +#endif + #define MAXARGS 20 /* maximum number of arguments allowed */ const char * volatile interp_identifier; @@ -79,6 +83,10 @@ interact(void) input[0] = '\0'; interp_emit_prompt(); ngets(input, sizeof(input)); +#ifdef LOADER_VERIEXEC + /* some settings should be restritcted */ + ve_status_set(-1, VE_UNVERIFIED_OK); +#endif interp_run(input); } } diff --git a/stand/efi/loader/version.veriexec b/stand/efi/loader/version.veriexec new file mode 100644 index 000000000000..5c9292310c04 --- /dev/null +++ b/stand/efi/loader/version.veriexec @@ -0,0 +1,7 @@ +NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this +file is important. Make sure the current version number is on line 6. + +2.1: SMBIOS 3 support +2.0: Secure boot support +1.1: Keep in sync with i386 version. +0.1: Initial i386 version. Derived from ia64. diff --git a/stand/veriexec.mk b/stand/veriexec.mk index 930e933be0a9..a0ff7d1e8489 100644 --- a/stand/veriexec.mk +++ b/stand/veriexec.mk @@ -1,4 +1,7 @@ .if ${MK_LOADER_VERIEXEC} != "no" +.if exists(${VERSION_FILE}.veriexec) +VERSION_FILE:= ${VERSION_FILE}.veriexec +.endif CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h .if ${MK_LOADER_VERIEXEC_VECTX} != "no" CFLAGS+= -DLOADER_VERIEXEC_VECTX