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>