Changeset View
Standalone View
lib/libsecureboot/vets.c
Show First 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
#define SECONDS_PER_DAY 86400 | #define SECONDS_PER_DAY 86400 | ||||
#define X509_DAYS_TO_UTC0 719528 | #define X509_DAYS_TO_UTC0 719528 | ||||
int DebugVe = 0; | int DebugVe = 0; | ||||
typedef VECTOR(br_x509_certificate) cert_list; | typedef VECTOR(br_x509_certificate) cert_list; | ||||
typedef VECTOR(hash_data) digest_list; | |||||
static anchor_list trust_anchors = VEC_INIT; | static anchor_list trust_anchors = VEC_INIT; | ||||
static anchor_list forbidden_anchors = VEC_INIT; | |||||
static digest_list forbidden_digests = VEC_INIT; | |||||
void | void | ||||
ve_debug_set(int n) | ve_debug_set(int n) | ||||
{ | { | ||||
DebugVe = n; | DebugVe = n; | ||||
} | } | ||||
static char ebuf[512]; | static char ebuf[512]; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
free_cert_contents(br_x509_certificate *xc) | free_cert_contents(br_x509_certificate *xc) | ||||
{ | { | ||||
xfree(xc->data); | xfree(xc->data); | ||||
} | } | ||||
/** | /* ASN parsing related defines */ | ||||
* @brief | #define ASN1_CONSTRUCTED BIT(5) | ||||
* add certs to our trust store | #define ASN1_PRIMITIVE_TAG 0x1F | ||||
#define ASN1_INF_LENGTH 0x80 | |||||
sjg: alignment needs cleanup | |||||
Done Inline ActionsSorry, I went through the entire patch and cleaned up everything I noticed, should be good now. kd: Sorry, I went through the entire patch and cleaned up everything I noticed, should be good now. | |||||
#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 | static void* | ||||
ve_trust_anchors_add(br_x509_certificate *xcs, size_t num) | X509_to_tbs(unsigned char* cert, size_t* output_size) | ||||
{ | { | ||||
unsigned char *result; | |||||
size_t tbs_size; | |||||
int size; | |||||
int 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; | br_x509_trust_anchor ta; | ||||
size_t u; | size_t u; | ||||
for (u = 0; u < num; u++) { | for (u = 0; u < num; u++) { | ||||
if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) { | if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) { | ||||
break; | break; | ||||
} | } | ||||
VEC_ADD(trust_anchors, ta); | VEC_ADD(*anchors, ta); | ||||
} | } | ||||
return (u); | return (u); | ||||
} | } | ||||
/** | /** | ||||
* @brief | * @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 | * initialize our trust_anchors from ta_PEM | ||||
*/ | */ | ||||
int | int | ||||
ve_trust_init(void) | ve_trust_init(void) | ||||
{ | { | ||||
#ifdef TRUST_ANCHOR_STR | |||||
br_x509_certificate *xcs; | br_x509_certificate *xcs; | ||||
#endif | |||||
static int once = -1; | static int once = -1; | ||||
size_t num; | size_t num; | ||||
if (once >= 0) | if (once >= 0) | ||||
return (once); | return (once); | ||||
once = 0; | |||||
ve_utc_set(time(NULL)); | ve_utc_set(time(NULL)); | ||||
#ifdef BUILD_UTC | #ifdef BUILD_UTC | ||||
ve_utc_set(BUILD_UTC); /* just in case */ | ve_utc_set(BUILD_UTC); /* just in case */ | ||||
#endif | #endif | ||||
ve_error_set(NULL); /* make sure it is empty */ | ve_error_set(NULL); /* make sure it is empty */ | ||||
#ifdef VE_PCR_SUPPORT | #ifdef VE_PCR_SUPPORT | ||||
ve_pcr_init(); | ve_pcr_init(); | ||||
#endif | #endif | ||||
#ifdef TRUST_ANCHOR_STR | #ifdef TRUST_ANCHOR_STR | ||||
xcs = parse_certificates(__DECONST(unsigned char *, TRUST_ANCHOR_STR), | xcs = parse_certificates(__DECONST(unsigned char *, TRUST_ANCHOR_STR), | ||||
sizeof(TRUST_ANCHOR_STR), &num); | sizeof(TRUST_ANCHOR_STR), &num); | ||||
if (xcs == NULL) | if (xcs == NULL) { | ||||
return (0); | once = (int) VEC_LEN(trust_anchors); | ||||
return (once); | |||||
} | |||||
num = ve_trust_anchors_add(xcs, num); | num = ve_trust_anchors_add(xcs, num); | ||||
once = (int) num; | |||||
#else | |||||
num = 0; | |||||
#endif | #endif | ||||
return (num); | once = (int) VEC_LEN(trust_anchors); | ||||
return (once); | |||||
} | } | ||||
/** | /** | ||||
* if we can verify the certificate chain in "certs", | * if we can verify the certificate chain in "certs", | ||||
* return the public key and if "xcp" is !NULL the associated | * return the public key and if "xcp" is !NULL the associated | ||||
* certificate | * certificate | ||||
*/ | */ | ||||
static br_x509_pkey * | static br_x509_pkey * | ||||
verify_signer_xcs(br_x509_certificate *xcs, | verify_signer_xcs(br_x509_certificate *xcs, | ||||
size_t num, | 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_minimal_context mc; | ||||
br_x509_certificate *xc; | br_x509_certificate *xc; | ||||
size_t u; | size_t u; | ||||
cert_list chain = VEC_INIT; | cert_list chain = VEC_INIT; | ||||
const br_x509_pkey *tpk; | const br_x509_pkey *tpk; | ||||
br_x509_pkey *pk; | br_x509_pkey *pk; | ||||
unsigned int usages; | unsigned int usages; | ||||
int err; | int err; | ||||
DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num)); | DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num)); | ||||
VEC_ADDMANY(chain, xcs, num); | VEC_ADDMANY(chain, xcs, num); | ||||
if (VEC_LEN(chain) == 0) { | if (VEC_LEN(chain) == 0) { | ||||
ve_error_set("ERROR: no/invalid certificate chain\n"); | ve_error_set("ERROR: no/invalid certificate chain\n"); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n", | DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n", | ||||
VEC_LEN(trust_anchors))); | VEC_LEN(*anchors))); | ||||
br_x509_minimal_init(&mc, &br_sha256_vtable, | br_x509_minimal_init(&mc, &br_sha256_vtable, | ||||
&VEC_ELT(trust_anchors, 0), | &VEC_ELT(*anchors, 0), | ||||
VEC_LEN(trust_anchors)); | VEC_LEN(*anchors)); | ||||
#ifdef VE_ECDSA_SUPPORT | #ifdef VE_ECDSA_SUPPORT | ||||
br_x509_minimal_set_ecdsa(&mc, | br_x509_minimal_set_ecdsa(&mc, | ||||
&br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1); | &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1); | ||||
#endif | #endif | ||||
#ifdef VE_RSA_SUPPORT | #ifdef VE_RSA_SUPPORT | ||||
br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy); | br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy); | ||||
#endif | #endif | ||||
#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT) | #if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT) | ||||
Show All 38 Lines | #endif | ||||
if (err) { | if (err) { | ||||
ve_error_set("Validation failed, err = %d", err); | ve_error_set("Validation failed, err = %d", err); | ||||
} else { | } else { | ||||
tpk = mc.vtable->get_pkey(&mc.vtable, &usages); | tpk = mc.vtable->get_pkey(&mc.vtable, &usages); | ||||
if (tpk != NULL) { | if (tpk != NULL) { | ||||
pk = xpkeydup(tpk); | pk = xpkeydup(tpk); | ||||
} | } | ||||
} | } | ||||
VEC_CLEAREXT(chain, &free_cert_contents); | VEC_CLEAR(chain); | ||||
return (pk); | return (pk); | ||||
} | } | ||||
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_cert; | |||||
hash_data *digest; | |||||
br_hash_compat_context ctx; | |||||
const br_hash_class *md; | |||||
size_t tbs_size; | |||||
size_t i; | |||||
if (VEC_LEN(forbidden_digests) == 0) | |||||
return (0); | |||||
while (num--) { | |||||
Not Done Inline ActionsI think a comment is needed to explain what you are trying to achieve, sjg: I think a comment is needed to explain what you are trying to achieve,
since I'm still not… | |||||
Done Inline ActionsAdded some comments to clarify this. kd: Added some comments to clarify this.
Basically UEFI standard allows to store three types of… | |||||
tbs_cert = X509_to_tbs(xcs[num].data, &tbs_size); | |||||
md = &br_sha256_vtable; | |||||
Not Done Inline Actionsthis seems wasteful. Why not compute the hash after you know which type it needs to be compared to? sjg: this seems wasteful. Why not compute the hash after you know which type it needs to be… | |||||
Done Inline ActionsChanged it so that now digests are calculated when necessary. Thanks. kd: Changed it so that now digests are calculated when necessary. Thanks. | |||||
md->init(&ctx.vtable); | |||||
md->update(&ctx.vtable, tbs_cert, tbs_size); | |||||
md->out(&ctx.vtable, sha256_digest); | |||||
md = &br_sha384_vtable; | |||||
md->init(&ctx.vtable); | |||||
md->update(&ctx.vtable, tbs_cert, tbs_size); | |||||
md->out(&ctx.vtable, sha384_digest); | |||||
md = &br_sha512_vtable; | |||||
md->init(&ctx.vtable); | |||||
md->update(&ctx.vtable, tbs_cert, tbs_size); | |||||
md->out(&ctx.vtable, sha512_digest); | |||||
for (i = 0; i < VEC_LEN(forbidden_digests); i++) { | |||||
Not Done Inline Actionsspace after for sjg: space after for | |||||
Done Inline ActionsThanks. kd: Thanks. | |||||
digest = &VEC_ELT(forbidden_digests, i); | |||||
switch (digest->digest_size) { | |||||
case br_sha256_SIZE: | |||||
if (!memcmp(sha256_digest, | |||||
digest->data, | |||||
br_sha256_SIZE)) | |||||
return (1); | |||||
break; | |||||
case br_sha384_SIZE: | |||||
if (!memcmp(sha384_digest, | |||||
digest->data, | |||||
br_sha384_SIZE)) | |||||
return (1); | |||||
break; | |||||
case br_sha512_SIZE: | |||||
if (!memcmp(sha512_digest, | |||||
digest->data, | |||||
br_sha512_SIZE)) | |||||
return (1); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return (0); | |||||
} | |||||
static br_x509_pkey * | static br_x509_pkey * | ||||
verify_signer(const char *certs, | verify_signer(const char *certs, | ||||
br_name_element *elts, size_t num_elts) | br_name_element *elts, size_t num_elts) | ||||
{ | { | ||||
br_x509_certificate *xcs; | br_x509_certificate *xcs; | ||||
br_x509_pkey *pk; | br_x509_pkey *pk; | ||||
size_t num; | size_t num; | ||||
pk = NULL; | |||||
ve_trust_init(); | ve_trust_init(); | ||||
xcs = read_certificates(certs, &num); | xcs = read_certificates(certs, &num); | ||||
if (xcs == NULL) { | if (xcs == NULL) { | ||||
ve_error_set("cannot read certificates\n"); | ve_error_set("cannot read certificates\n"); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
pk = verify_signer_xcs(xcs, num, elts, num_elts); | /* | ||||
* Check if hash of tbs part of any certificate in chain | |||||
* is on the forbidden list. | |||||
*/ | |||||
if (check_forbidden_digests(xcs, num)) { | |||||
Not Done Inline ActionsThis call really depends on use of EFI for trust store. sjg: This call really depends on use of EFI for trust store.
Since as currently implemented it adds… | |||||
Done Inline ActionsI made checking for forbidden digests to be the last call. IMO in the most popular case - successful verification it won't matter since we have to perform all these checks. kd: I made checking for forbidden digests to be the last call. IMO in the most popular case… | |||||
Not Done Inline ActionsUnless you are using EFI though the forbidden checks make no sense because there are not going to be any. I'd like to avoid any unnecessary overhead. sjg: Unless you are using EFI though the forbidden checks make no sense because there are not going… | |||||
Done Inline ActionsI suppose that they can be put inside an ifdef to call them only when library is compiled with EFI support. kd: I suppose that they can be put inside an ifdef to call them only when library is compiled with… | |||||
ve_error_set("Certificate hash is on forbidden list\n"); | |||||
goto out; | |||||
} | |||||
/* | |||||
* 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); | |||||
out: | |||||
while(num--) | |||||
free_cert_contents(&xcs[num]); | |||||
xfree(xcs); | xfree(xcs); | ||||
return (pk); | return (pk); | ||||
} | } | ||||
/** | /** | ||||
* we need a hex digest including trailing newline below | * we need a hex digest including trailing newline below | ||||
*/ | */ | ||||
char * | char * | ||||
▲ Show 20 Lines • Show All 389 Lines • ▼ Show 20 Lines | #ifdef VERIFY_CERTS_STR | ||||
cn_oid[1] = 0x55; | cn_oid[1] = 0x55; | ||||
cn_oid[2] = 4; | cn_oid[2] = 4; | ||||
cn_oid[3] = 3; | cn_oid[3] = 3; | ||||
cn.oid = cn_oid; | cn.oid = cn_oid; | ||||
cn.buf = cn_buf; | cn.buf = cn_buf; | ||||
for (u = 0; u < num; u ++) { | for (u = 0; u < num; u ++) { | ||||
cn.len = sizeof(cn_buf); | 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++; | once++; | ||||
printf("Testing verify certificate: %s\tPassed\n", | printf("Testing verify certificate: %s\tPassed\n", | ||||
cn.status ? cn_buf : ""); | cn.status ? cn_buf : ""); | ||||
xfreepkey(pk); | xfreepkey(pk); | ||||
} | } | ||||
} | } | ||||
if (!once) | if (!once) | ||||
printf("Testing verify certificate:\t\t\tFailed\n"); | printf("Testing verify certificate:\t\t\tFailed\n"); | ||||
Show All 10 Lines |
alignment needs cleanup