Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 Apr 2019 00:48:53 +0000 (UTC)
From:      Marcin Wojtas <mw@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r346719 - in stable/12: lib/libsecureboot lib/libsecureboot/efi lib/libsecureboot/h share/mk stand/efi/loader tools/build/options
Message-ID:  <201904260048.x3Q0mr2o086036@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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@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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201904260048.x3Q0mr2o086036>