svn commit: r346719 - in stable/12: lib/libsecureboot lib/libsecureboot/efi lib/libsecureboot/h share/mk stand/efi/loader tools/build/options
Marcin Wojtas
mw at FreeBSD.org
Fri Apr 26 00:48:56 UTC 2019
Author: mw
Date: Fri Apr 26 00:48:52 2019
New Revision: 346719
URL: https://svnweb.freebsd.org/changeset/base/346719
Log:
MFC r344840: Extend libsecureboot(old libve) to obtain trusted certificates from UEFI and implement revocation
UEFI related headers were copied from edk2.
A new build option "MK_LOADER_EFI_SECUREBOOT" was added to allow
loading of trusted anchors from UEFI.
Certificate revocation support is also introduced.
The forbidden certificates are loaded from dbx variable.
Verification fails in two cases:
There is a direct match between cert in dbx and the one in the chain.
The CA used to sign the chain is found in dbx.
One can also insert a hash of TBS section of a certificate into dbx.
In this case verifications fails only if a direct match with a
certificate in chain is found.
Submitted by: Kornel Duleba <mindal at semihalf.com>
Obtained from: Semihalf
Sponsored by: Stormshield
Added:
stable/12/lib/libsecureboot/efi/
- copied from r344840, head/lib/libsecureboot/efi/
stable/12/tools/build/options/WITH_LOADER_EFI_SECUREBOOT
- copied unchanged from r344840, head/tools/build/options/WITH_LOADER_EFI_SECUREBOOT
Modified:
stable/12/lib/libsecureboot/Makefile.inc
stable/12/lib/libsecureboot/Makefile.libsa.inc
stable/12/lib/libsecureboot/h/verify_file.h
stable/12/lib/libsecureboot/libsecureboot-priv.h
stable/12/lib/libsecureboot/local.trust.mk
stable/12/lib/libsecureboot/verify_file.c
stable/12/lib/libsecureboot/vets.c
stable/12/share/mk/src.opts.mk
stable/12/stand/efi/loader/Makefile
stable/12/stand/efi/loader/main.c
Modified: stable/12/lib/libsecureboot/Makefile.inc
==============================================================================
--- stable/12/lib/libsecureboot/Makefile.inc Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/Makefile.inc Fri Apr 26 00:48:52 2019 (r346719)
@@ -31,6 +31,17 @@ BRSSL_SRCS+= \
${BEARSSL}/tools/xmem.c \
${BEARSSL}/tools/vector.c
+BRSSL_DEPS= \
+ brf.c \
+ vets.c \
+ veta.c
+
+.if ${MK_LOADER_EFI_SECUREBOOT} != "no"
+BRSSL_DEPS+= \
+ efi_init.c \
+ efi_variables.c
+.endif
+
# we do not need/want nested objdirs
OBJS_SRCS_FILTER = T R
@@ -134,7 +145,7 @@ vse.h:
echo 'NULL };' ) > ${.TARGET}
-.for s in ${BRSSL_SRCS} brf.c vets.c veta.c
+.for s in ${BRSSL_SRCS} ${BRSSL_DEPS}
.ifdef BRSSL_SED
$s: brssl.h
.endif
Modified: stable/12/lib/libsecureboot/Makefile.libsa.inc
==============================================================================
--- stable/12/lib/libsecureboot/Makefile.libsa.inc Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/Makefile.libsa.inc Fri Apr 26 00:48:52 2019 (r346719)
@@ -16,6 +16,19 @@ SRCS+= \
vepcr.c \
verify_file.c \
+# Build library with support for the UEFI based authentication
+.if ${MK_LOADER_EFI_SECUREBOOT} == "yes"
+SRCS+= \
+ efi/efi_variables.c \
+ efi/efi_init.c
+
+# Add includes required by efi part
+CFLAGS+= \
+ -I${SRCTOP}/stand/efi/include \
+ -I${SRCTOP}/lib/libsecureboot/efi/include \
+ -I${SRCTOP}/stand/efi/include/${MACHINE}
+.endif
+
# this is the list of paths (relative to a file
# that we need to verify) used to find a signed manifest.
# the signature extensions in VE_SIGNATURE_EXT_LIST
Modified: stable/12/lib/libsecureboot/h/verify_file.h
==============================================================================
--- stable/12/lib/libsecureboot/h/verify_file.h Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/h/verify_file.h Fri Apr 26 00:48:52 2019 (r346719)
@@ -40,6 +40,7 @@ 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 verify_file(int, const char *, off_t, int);
void verify_pcr_export(void);
Modified: stable/12/lib/libsecureboot/libsecureboot-priv.h
==============================================================================
--- stable/12/lib/libsecureboot/libsecureboot-priv.h Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/libsecureboot-priv.h Fri Apr 26 00:48:52 2019 (r346719)
@@ -31,8 +31,15 @@
/* public api */
#include "libsecureboot.h"
+typedef struct {
+ unsigned char *data;
+ size_t hash_size;
+} hash_data;
+
size_t ve_trust_anchors_add(br_x509_certificate *, size_t);
-char *fingerprint_info_lookup(int, const char *);
+size_t ve_forbidden_anchors_add(br_x509_certificate *, size_t);
+void ve_forbidden_digest_add(hash_data *digest, size_t);
+char *fingerprint_info_lookup(int, const char *);
br_x509_certificate * parse_certificates(unsigned char *, size_t, size_t *);
int certificate_to_trust_anchor_inner(br_x509_trust_anchor *,
@@ -44,5 +51,10 @@ int verify_rsa_digest(br_rsa_public_key *pkey,
unsigned char *sdata, size_t slen);
int openpgp_self_tests(void);
+
+int efi_secure_boot_enabled(void);
+br_x509_certificate* efi_get_trusted_certs(size_t *count);
+br_x509_certificate* efi_get_forbidden_certs(size_t *count);
+hash_data* efi_get_forbidden_digests(size_t *count);
#endif /* _LIBSECUREBOOT_PRIV_H_ */
Modified: stable/12/lib/libsecureboot/local.trust.mk
==============================================================================
--- stable/12/lib/libsecureboot/local.trust.mk Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/local.trust.mk Fri Apr 26 00:48:52 2019 (r346719)
@@ -7,27 +7,26 @@
# for each key will provide the appropriate certificate chain on request
# force these for Junos
-MANIFEST_SKIP_ALWAYS= boot
+#MANIFEST_SKIP_ALWAYS= boot
VE_HASH_LIST= \
SHA1 \
SHA256 \
- SHA384
+ SHA384 \
+ SHA512
VE_SIGNATURE_LIST= \
- ECDSA
+ ECDSA \
+ RSA
VE_SIGNATURE_EXT_LIST= \
- esig
+ esig \
+ rsig
VE_SELF_TESTS= yes
.if ${MACHINE} == "host" && ${.CURDIR:T} == "tests"
-# for testing
-VE_HASH_LIST+= \
- SHA512
VE_SIGNATURE_LIST+= \
- RSA \
DEPRECATED_RSA_SHA1
VE_SIGNATURE_EXT_LIST+= \
@@ -88,7 +87,7 @@ vc_rsa.pem: rcerts.pem _2ndLAST_PEM_USE
.endif
# we take the mtime of this as our baseline time
-BUILD_UTC_FILE= ecerts.pem
+#BUILD_UTC_FILE= ecerts.pem
#VE_DEBUG_LEVEL=3
#VE_VERBOSE_DEFAULT=1
@@ -97,7 +96,7 @@ BUILD_UTC_FILE= ecerts.pem
.if empty(TRUST_ANCHORS)
TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null
.endif
-.if empty(TRUST_ANCHORS)
+.if empty(TRUST_ANCHORS) && ${MK_LOADER_EFI_SECUREBOOT} != "yes"
.error Need TRUST_ANCHORS see ${.CURDIR}/README.rst
.endif
.if ${TRUST_ANCHORS:T:Mt*.pem} != ""
Modified: stable/12/lib/libsecureboot/verify_file.c
==============================================================================
--- stable/12/lib/libsecureboot/verify_file.c Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/verify_file.c Fri Apr 26 00:48:52 2019 (r346719)
@@ -346,7 +346,7 @@ verify_file(int fd, const char *filename, off_t off, i
if (verbose || severity > VE_WANT) {
#if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0
printf("Verified %s %llu,%llu\n", filename,
- st.st_dev, st.st_ino);
+ (long long)st.st_dev, (long long)st.st_ino);
#else
printf("Verified %s\n", filename);
#endif
Modified: stable/12/lib/libsecureboot/vets.c
==============================================================================
--- stable/12/lib/libsecureboot/vets.c Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/lib/libsecureboot/vets.c Fri Apr 26 00:48:52 2019 (r346719)
@@ -49,8 +49,11 @@ __FBSDID("$FreeBSD$");
int DebugVe = 0;
typedef VECTOR(br_x509_certificate) cert_list;
+typedef VECTOR(hash_data) digest_list;
static anchor_list trust_anchors = VEC_INIT;
+static anchor_list forbidden_anchors = VEC_INIT;
+static digest_list forbidden_digests = VEC_INIT;
void
ve_debug_set(int n)
@@ -113,13 +116,76 @@ free_cert_contents(br_x509_certificate *xc)
xfree(xc->data);
}
-/**
- * @brief
- * add certs to our trust store
+/* ASN parsing related defines */
+#define ASN1_PRIMITIVE_TAG 0x1F
+#define ASN1_INF_LENGTH 0x80
+#define ASN1_LENGTH_MASK 0x7F
+
+/*
+ * Get TBS part of certificate.
+ * Since BearSSL doesn't provide any API to do this,
+ * it has to be implemented here.
*/
-size_t
-ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
+static void*
+X509_to_tbs(unsigned char* cert, size_t* output_size)
{
+ unsigned char *result;
+ size_t tbs_size;
+ int size, i;
+
+ if (cert == NULL)
+ return (NULL);
+
+ /* Strip two sequences to get to the TBS section */
+ for (i = 0; i < 2; i++) {
+ /*
+ * XXX: We don't need to support extended tags since
+ * they should not be present in certificates.
+ */
+ if ((*cert & ASN1_PRIMITIVE_TAG) == ASN1_PRIMITIVE_TAG)
+ return (NULL);
+
+ cert++;
+
+ if (*cert == ASN1_INF_LENGTH)
+ return (NULL);
+
+ size = *cert & ASN1_LENGTH_MASK;
+ tbs_size = 0;
+
+ /* Size can either be stored on a single or multiple bytes */
+ if (*cert & (ASN1_LENGTH_MASK + 1)) {
+ cert++;
+ while (*cert == 0 && size > 0) {
+ cert++;
+ size--;
+ }
+ while (size-- > 0) {
+ tbs_size <<= 8;
+ tbs_size |= *(cert++);
+ }
+ }
+ if (i == 0)
+ result = cert;
+ }
+ tbs_size += (cert - result);
+
+ if (output_size != NULL)
+ *output_size = tbs_size;
+
+ return (result);
+}
+
+void
+ve_forbidden_digest_add(hash_data *digest, size_t num)
+{
+ while (num--)
+ VEC_ADD(forbidden_digests, digest[num]);
+}
+
+static size_t
+ve_anchors_add(br_x509_certificate *xcs, size_t num, anchor_list *anchors)
+{
br_x509_trust_anchor ta;
size_t u;
@@ -127,25 +193,42 @@ ve_trust_anchors_add(br_x509_certificate *xcs, size_t
if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
break;
}
- VEC_ADD(trust_anchors, ta);
+ VEC_ADD(*anchors, ta);
}
return (u);
}
/**
* @brief
+ * add certs to our trust store
+ */
+size_t
+ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
+{
+ return (ve_anchors_add(xcs, num, &trust_anchors));
+}
+
+size_t
+ve_forbidden_anchors_add(br_x509_certificate *xcs, size_t num)
+{
+ return (ve_anchors_add(xcs, num, &forbidden_anchors));
+}
+
+/**
+ * @brief
* initialize our trust_anchors from ta_PEM
*/
int
ve_trust_init(void)
{
+#ifdef TRUST_ANCHOR_STR
br_x509_certificate *xcs;
+#endif
static int once = -1;
size_t num;
if (once >= 0)
return (once);
- once = 0;
ve_utc_set(time(NULL));
#ifdef BUILD_UTC
@@ -159,14 +242,12 @@ ve_trust_init(void)
#ifdef TRUST_ANCHOR_STR
xcs = parse_certificates(__DECONST(unsigned char *, TRUST_ANCHOR_STR),
sizeof(TRUST_ANCHOR_STR), &num);
- if (xcs == NULL)
- return (0);
- num = ve_trust_anchors_add(xcs, num);
- once = (int) num;
-#else
- num = 0;
+ if (xcs != NULL)
+ num = ve_trust_anchors_add(xcs, num);
#endif
- return (num);
+ once = (int) VEC_LEN(trust_anchors);
+
+ return (once);
}
/**
@@ -177,7 +258,8 @@ ve_trust_init(void)
static br_x509_pkey *
verify_signer_xcs(br_x509_certificate *xcs,
size_t num,
- br_name_element *elts, size_t num_elts)
+ br_name_element *elts, size_t num_elts,
+ anchor_list *anchors)
{
br_x509_minimal_context mc;
br_x509_certificate *xc;
@@ -196,11 +278,11 @@ verify_signer_xcs(br_x509_certificate *xcs,
}
DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n",
- VEC_LEN(trust_anchors)));
+ VEC_LEN(*anchors)));
br_x509_minimal_init(&mc, &br_sha256_vtable,
- &VEC_ELT(trust_anchors, 0),
- VEC_LEN(trust_anchors));
+ &VEC_ELT(*anchors, 0),
+ VEC_LEN(*anchors));
#ifdef VE_ECDSA_SUPPORT
br_x509_minimal_set_ecdsa(&mc,
&br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
@@ -255,10 +337,96 @@ verify_signer_xcs(br_x509_certificate *xcs,
pk = xpkeydup(tpk);
}
}
- VEC_CLEAREXT(chain, &free_cert_contents);
+ VEC_CLEAR(chain);
return (pk);
}
+/*
+ * Check if digest of one of the certificates from verified chain
+ * is present in the forbidden database.
+ * Since UEFI allows to store three types of digests
+ * all of them have to be checked separately.
+ */
+static int
+check_forbidden_digests(br_x509_certificate *xcs, size_t num)
+{
+ unsigned char sha256_digest[br_sha256_SIZE];
+ unsigned char sha384_digest[br_sha384_SIZE];
+ unsigned char sha512_digest[br_sha512_SIZE];
+ void *tbs;
+ hash_data *digest;
+ br_hash_compat_context ctx;
+ const br_hash_class *md;
+ size_t tbs_len, i;
+ int have_sha256, have_sha384, have_sha512;
+
+ if (VEC_LEN(forbidden_digests) == 0)
+ return (0);
+
+ /*
+ * Iterate through certificates, extract their To-Be-Signed section,
+ * and compare its digest against the ones in the forbidden database.
+ */
+ while (num--) {
+ tbs = X509_to_tbs(xcs[num].data, &tbs_len);
+ if (tbs == NULL) {
+ printf("Failed to obtain TBS part of certificate\n");
+ return (1);
+ }
+ have_sha256 = have_sha384 = have_sha512 = 0;
+
+ for (i = 0; i < VEC_LEN(forbidden_digests); i++) {
+ digest = &VEC_ELT(forbidden_digests, i);
+ switch (digest->hash_size) {
+ case br_sha256_SIZE:
+ if (!have_sha256) {
+ have_sha256 = 1;
+ md = &br_sha256_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha256_digest);
+ }
+ if (!memcmp(sha256_digest,
+ digest->data,
+ br_sha256_SIZE))
+ return (1);
+
+ break;
+ case br_sha384_SIZE:
+ if (!have_sha384) {
+ have_sha384 = 1;
+ md = &br_sha384_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha384_digest);
+ }
+ if (!memcmp(sha384_digest,
+ digest->data,
+ br_sha384_SIZE))
+ return (1);
+
+ break;
+ case br_sha512_SIZE:
+ if (!have_sha512) {
+ have_sha512 = 1;
+ md = &br_sha512_vtable;
+ md->init(&ctx.vtable);
+ md->update(&ctx.vtable, tbs, tbs_len);
+ md->out(&ctx.vtable, sha512_digest);
+ }
+ if (!memcmp(sha512_digest,
+ digest->data,
+ br_sha512_SIZE))
+ return (1);
+
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
+
static br_x509_pkey *
verify_signer(const char *certs,
br_name_element *elts, size_t num_elts)
@@ -266,15 +434,46 @@ verify_signer(const char *certs,
br_x509_certificate *xcs;
br_x509_pkey *pk;
size_t num;
-
+
+ pk = NULL;
+
ve_trust_init();
xcs = read_certificates(certs, &num);
if (xcs == NULL) {
ve_error_set("cannot read certificates\n");
return (NULL);
}
- pk = verify_signer_xcs(xcs, num, elts, num_elts);
- xfree(xcs);
+
+ /*
+ * Check if either
+ * 1. There is a direct match between cert from forbidden_anchors
+ * and a cert from chain.
+ * 2. CA that signed the chain is found in forbidden_anchors.
+ */
+ if (VEC_LEN(forbidden_anchors) > 0)
+ pk = verify_signer_xcs(xcs, num, elts, num_elts, &forbidden_anchors);
+ if (pk != NULL) {
+ ve_error_set("Certificate is on forbidden list\n");
+ xfreepkey(pk);
+ pk = NULL;
+ goto out;
+ }
+
+ pk = verify_signer_xcs(xcs, num, elts, num_elts, &trust_anchors);
+ if (pk == NULL)
+ goto out;
+
+ /*
+ * Check if hash of tbs part of any certificate in chain
+ * is on the forbidden list.
+ */
+ if (check_forbidden_digests(xcs, num)) {
+ ve_error_set("Certificate hash is on forbidden list\n");
+ xfreepkey(pk);
+ pk = NULL;
+ }
+out:
+ free_certificates(xcs, num);
return (pk);
}
@@ -679,7 +878,8 @@ ve_self_tests(void)
for (u = 0; u < num; u ++) {
cn.len = sizeof(cn_buf);
- if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1)) != NULL) {
+ if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1, &trust_anchors)) != NULL) {
+ free_cert_contents(&xcs[u]);
once++;
printf("Testing verify certificate: %s\tPassed\n",
cn.status ? cn_buf : "");
Modified: stable/12/share/mk/src.opts.mk
==============================================================================
--- stable/12/share/mk/src.opts.mk Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/share/mk/src.opts.mk Fri Apr 26 00:48:52 2019 (r346719)
@@ -216,6 +216,7 @@ __DEFAULT_DEPENDENT_OPTIONS= \
CLANG_FULL/CLANG \
LLVM_TARGET_ALL/CLANG \
LOADER_VERIEXEC/BEARSSL \
+ LOADER_EFI_SECUREBOOT/LOADER_VERIEXEC \
VERIEXEC/BEARSSL \
# MK_*_SUPPORT options which default to "yes" unless their corresponding
Modified: stable/12/stand/efi/loader/Makefile
==============================================================================
--- stable/12/stand/efi/loader/Makefile Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/stand/efi/loader/Makefile Fri Apr 26 00:48:52 2019 (r346719)
@@ -81,6 +81,10 @@ HAVE_BCACHE= yes
CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE}
.endif
+.if ${MK_LOADER_EFI_SECUREBOOT} != "no"
+CFLAGS+= -DEFI_SECUREBOOT
+.endif
+
NEWVERSWHAT= "EFI loader" ${MACHINE}
VERSION_FILE= ${.CURDIR}/../loader/version
Modified: stable/12/stand/efi/loader/main.c
==============================================================================
--- stable/12/stand/efi/loader/main.c Fri Apr 26 00:39:30 2019 (r346718)
+++ stable/12/stand/efi/loader/main.c Fri Apr 26 00:48:52 2019 (r346719)
@@ -963,6 +963,17 @@ main(int argc, CHAR16 *argv[])
BS->SetWatchdogTimer(0, 0, 0, NULL);
/*
+ * Initialize the trusted/forbidden certificates from UEFI.
+ * They will be later used to verify the manifest(s),
+ * which should contain hashes of verified files.
+ * This needs to be initialized before any configuration files
+ * are loaded.
+ */
+#ifdef EFI_SECUREBOOT
+ ve_efi_init();
+#endif
+
+ /*
* Try and find a good currdev based on the image that was booted.
* It might be desirable here to have a short pause to allow falling
* through to the boot loader instead of returning instantly to follow
Copied: stable/12/tools/build/options/WITH_LOADER_EFI_SECUREBOOT (from r344840, head/tools/build/options/WITH_LOADER_EFI_SECUREBOOT)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/12/tools/build/options/WITH_LOADER_EFI_SECUREBOOT Fri Apr 26 00:48:52 2019 (r346719, copy of r344840, head/tools/build/options/WITH_LOADER_EFI_SECUREBOOT)
@@ -0,0 +1,5 @@
+.\" $FreeBSD$
+Enable building
+.Xr loader 8
+with support for verification based on certificates obtained from UEFI.
+.Pp
More information about the svn-src-stable-12
mailing list