diff options
author | Ilias Apalodimas <ilias.apalodimas@linaro.org> | 2022-05-06 15:36:00 +0300 |
---|---|---|
committer | Heinrich Schuchardt <heinrich.schuchardt@canonical.com> | 2022-05-07 23:17:26 +0200 |
commit | b436cc6a57cae017343a549f4b701e748d7e6448 (patch) | |
tree | b109ed047f6936b107e135b04c0e937cd912013a /lib | |
parent | 3ae6cf5400ee004c309f73f358c1043cf6d8eecc (diff) | |
download | u-boot-b436cc6a57cae017343a549f4b701e748d7e6448.tar.gz u-boot-b436cc6a57cae017343a549f4b701e748d7e6448.tar.bz2 u-boot-b436cc6a57cae017343a549f4b701e748d7e6448.zip |
efi_loader: add sha384/512 on certificate revocation
Currently we don't support sha384/512 for the X.509 certificate
in dbx. Moreover if we come across such a hash we skip the check
and approve the image, although the image might needs to be rejected.
Rework the code a bit and fix it by adding an array of structs with the
supported GUIDs, len and literal used in the U-Boot crypto APIs instead
of hardcoding the GUID types.
It's worth noting here that efi_hash_regions() can now be reused from
efi_signature_lookup_digest() and add sha348/512 support there as well
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/efi_loader/efi_helper.c | 66 | ||||
-rw-r--r-- | lib/efi_loader/efi_signature.c | 76 |
2 files changed, 119 insertions, 23 deletions
diff --git a/lib/efi_loader/efi_helper.c b/lib/efi_loader/efi_helper.c index 802d39ed97..c4499f65ee 100644 --- a/lib/efi_loader/efi_helper.c +++ b/lib/efi_loader/efi_helper.c @@ -92,3 +92,69 @@ err: free(var_value); return NULL; } + +const struct guid_to_hash_map { + efi_guid_t guid; + const char algo[32]; + u32 bits; +} guid_to_hash[] = { + { + EFI_CERT_X509_SHA256_GUID, + "sha256", + SHA256_SUM_LEN * 8, + }, + { + EFI_CERT_SHA256_GUID, + "sha256", + SHA256_SUM_LEN * 8, + }, + { + EFI_CERT_X509_SHA384_GUID, + "sha384", + SHA384_SUM_LEN * 8, + }, + { + EFI_CERT_X509_SHA512_GUID, + "sha512", + SHA512_SUM_LEN * 8, + }, +}; + +#define MAX_GUID_TO_HASH_COUNT ARRAY_SIZE(guid_to_hash) + +/** guid_to_sha_str - return the sha string e.g "sha256" for a given guid + * used on EFI security databases + * + * @guid: guid to check + * + * Return: len or 0 if no match is found + */ +const char *guid_to_sha_str(const efi_guid_t *guid) +{ + size_t i; + + for (i = 0; i < MAX_GUID_TO_HASH_COUNT; i++) { + if (!guidcmp(guid, &guid_to_hash[i].guid)) + return guid_to_hash[i].algo; + } + + return NULL; +} + +/** algo_to_len - return the sha size in bytes for a given string + * + * @algo: string indicating hashing algorithm to check + * + * Return: length of hash in bytes or 0 if no match is found + */ +int algo_to_len(const char *algo) +{ + size_t i; + + for (i = 0; i < MAX_GUID_TO_HASH_COUNT; i++) { + if (!strcmp(algo, guid_to_hash[i].algo)) + return guid_to_hash[i].bits / 8; + } + + return 0; +} diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 79ed077ae7..ddac751d12 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -24,6 +24,8 @@ const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID; const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID; const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID; const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID; +const efi_guid_t efi_guid_cert_x509_sha384 = EFI_CERT_X509_SHA384_GUID; +const efi_guid_t efi_guid_cert_x509_sha512 = EFI_CERT_X509_SHA512_GUID; const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; static u8 pkcs7_hdr[] = { @@ -124,23 +126,35 @@ struct pkcs7_message *efi_parse_pkcs7_header(const void *buf, * Return: true on success, false on error */ static bool efi_hash_regions(struct image_region *regs, int count, - void **hash, size_t *size) + void **hash, const char *hash_algo, int *len) { + int ret, hash_len; + + if (!hash_algo) + return false; + + hash_len = algo_to_len(hash_algo); + if (!hash_len) + return false; + if (!*hash) { - *hash = calloc(1, SHA256_SUM_LEN); + *hash = calloc(1, hash_len); if (!*hash) { EFI_PRINT("Out of memory\n"); return false; } } - if (size) - *size = SHA256_SUM_LEN; - hash_calculate("sha256", regs, count, *hash); + ret = hash_calculate(hash_algo, regs, count, *hash); + if (ret) + return false; + + if (len) + *len = hash_len; #ifdef DEBUG EFI_PRINT("hash calculated:\n"); print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, - *hash, SHA256_SUM_LEN, false); + *hash, hash_len, false); #endif return true; @@ -190,7 +204,6 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs, struct efi_signature_store *siglist; struct efi_sig_data *sig_data; void *hash = NULL; - size_t size = 0; bool found = false; bool hash_done = false; @@ -200,6 +213,8 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs, goto out; for (siglist = db; siglist; siglist = siglist->next) { + int len = 0; + const char *hash_algo = NULL; /* * if the hash algorithm is unsupported and we get an entry in * dbx reject the image @@ -215,8 +230,14 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs, if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) continue; + hash_algo = guid_to_sha_str(&efi_guid_sha256); + /* + * We could check size and hash_algo but efi_hash_regions() + * will do that for us + */ if (!hash_done && - !efi_hash_regions(regs->reg, regs->num, &hash, &size)) { + !efi_hash_regions(regs->reg, regs->num, &hash, hash_algo, + &len)) { EFI_PRINT("Digesting an image failed\n"); break; } @@ -229,8 +250,8 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs, print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, sig_data->data, sig_data->size, false); #endif - if (sig_data->size == size && - !memcmp(sig_data->data, hash, size)) { + if (sig_data->size == len && + !memcmp(sig_data->data, hash, len)) { found = true; free(hash); goto out; @@ -263,8 +284,9 @@ static bool efi_lookup_certificate(struct x509_certificate *cert, struct efi_sig_data *sig_data; struct image_region reg[1]; void *hash = NULL, *hash_tmp = NULL; - size_t size = 0; + int len = 0; bool found = false; + const char *hash_algo = NULL; EFI_PRINT("%s: Enter, %p, %p\n", __func__, cert, db); @@ -278,7 +300,10 @@ static bool efi_lookup_certificate(struct x509_certificate *cert, /* calculate hash of TBSCertificate */ reg[0].data = cert->tbs; reg[0].size = cert->tbs_size; - if (!efi_hash_regions(reg, 1, &hash, &size)) + + /* We just need any sha256 algo to start the matching */ + hash_algo = guid_to_sha_str(&efi_guid_sha256); + if (!efi_hash_regions(reg, 1, &hash, hash_algo, &len)) goto out; EFI_PRINT("%s: searching for %s\n", __func__, cert->subject); @@ -300,12 +325,13 @@ static bool efi_lookup_certificate(struct x509_certificate *cert, cert_tmp->subject); reg[0].data = cert_tmp->tbs; reg[0].size = cert_tmp->tbs_size; - if (!efi_hash_regions(reg, 1, &hash_tmp, NULL)) + if (!efi_hash_regions(reg, 1, &hash_tmp, hash_algo, + NULL)) goto out; x509_free_certificate(cert_tmp); - if (!memcmp(hash, hash_tmp, size)) { + if (!memcmp(hash, hash_tmp, len)) { found = true; goto out; } @@ -400,9 +426,10 @@ static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo, struct efi_sig_data *sig_data; struct image_region reg[1]; void *hash = NULL; - size_t size = 0; + int len = 0; time64_t revoc_time; bool revoked = false; + const char *hash_algo = NULL; EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx); @@ -411,13 +438,14 @@ static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo, EFI_PRINT("Checking revocation against %s\n", cert->subject); for (siglist = dbx; siglist; siglist = siglist->next) { - if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) + hash_algo = guid_to_sha_str(&siglist->sig_type); + if (!hash_algo) continue; /* calculate hash of TBSCertificate */ reg[0].data = cert->tbs; reg[0].size = cert->tbs_size; - if (!efi_hash_regions(reg, 1, &hash, &size)) + if (!efi_hash_regions(reg, 1, &hash, hash_algo, &len)) goto out; for (sig_data = siglist->sig_data_list; sig_data; @@ -429,18 +457,18 @@ static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo, * }; */ #ifdef DEBUG - if (sig_data->size >= size) { + if (sig_data->size >= len) { EFI_PRINT("hash in db:\n"); print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, - sig_data->data, size, false); + sig_data->data, len, false); } #endif - if ((sig_data->size < size + sizeof(time64_t)) || - memcmp(sig_data->data, hash, size)) + if ((sig_data->size < len + sizeof(time64_t)) || + memcmp(sig_data->data, hash, len)) continue; - memcpy(&revoc_time, sig_data->data + size, + memcpy(&revoc_time, sig_data->data + len, sizeof(revoc_time)); EFI_PRINT("revocation time: 0x%llx\n", revoc_time); /* @@ -500,7 +528,9 @@ bool efi_signature_verify(struct efi_image_regions *regs, */ if (!msg->data && !efi_hash_regions(regs->reg, regs->num, - (void **)&sinfo->sig->digest, NULL)) { + (void **)&sinfo->sig->digest, + guid_to_sha_str(&efi_guid_sha256), + NULL)) { EFI_PRINT("Digesting an image failed\n"); goto out; } |