Skip to content

Commit

Permalink
libsecureboot do not report expected unverified files
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sgerraty committed Feb 12, 2024
1 parent 57e27ff commit f616d61
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 102 deletions.
8 changes: 4 additions & 4 deletions lib/libsecureboot/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
7 changes: 5 additions & 2 deletions lib/libsecureboot/Makefile.libsa.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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}\"
Expand Down
1 change: 1 addition & 0 deletions lib/libsecureboot/h/verify_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down
143 changes: 62 additions & 81 deletions lib/libsecureboot/local.trust.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
# allow site control
.-include "site.trust.mk"

VE_SIGNATURE_EXT_LIST+= \
sig
.endif
#VE_DEBUG_LEVEL?=3
#VE_VERBOSE_DEFAULT?=2

# add OpenPGP support - possibly dormant
VE_SIGNATURE_LIST+= OPENPGP
VE_SIGNATURE_EXT_LIST+= asc
VE_HASH_LIST?= \
SHA256 \
SHA384 \

# allow site override of all the above
.-include "site.trust.mk"
VE_SELF_TESTS?= yes

SIGNER ?= ${SB_TOOLS_PATH:U/volume/buildtools/bin}/sign.py
# 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

# 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 \

.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

# deal with quirk of our .esig format
XCFLAGS.vets+= -DVE_ECDSA_HASH_AGAIN
# cleanup duplicates
VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:O:u}

.if !empty(OPENPGP_SIGN_URL)
.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
Expand All @@ -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
Expand All @@ -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

26 changes: 19 additions & 7 deletions lib/libsecureboot/vectx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 27 additions & 8 deletions lib/libsecureboot/verify_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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. */
Expand All @@ -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));
}


Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions stand/common/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include <string.h>
#include "bootstrap.h"

#ifdef LOADER_VERIEXEC
#include <verify_file.h>
#endif

#define MAXARGS 20 /* maximum number of arguments allowed */

const char * volatile interp_identifier;
Expand Down Expand Up @@ -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);
}
}
Expand Down
7 changes: 7 additions & 0 deletions stand/efi/loader/version.veriexec
Original file line number Diff line number Diff line change
@@ -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.
Loading

0 comments on commit f616d61

Please sign in to comment.