mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
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>
This commit is contained in:
parent
3ae6cf5400
commit
b436cc6a57
4 changed files with 131 additions and 23 deletions
|
@ -1873,6 +1873,12 @@ struct efi_system_resource_table {
|
||||||
#define EFI_CERT_X509_SHA256_GUID \
|
#define EFI_CERT_X509_SHA256_GUID \
|
||||||
EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
|
EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
|
||||||
0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
|
0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
|
||||||
|
#define EFI_CERT_X509_SHA384_GUID \
|
||||||
|
EFI_GUID(0x7076876e, 0x80c2, 0x4ee6, \
|
||||||
|
0xaa, 0xd2, 0x28, 0xb3, 0x49, 0xa6, 0x86, 0x5b)
|
||||||
|
#define EFI_CERT_X509_SHA512_GUID \
|
||||||
|
EFI_GUID(0x446dbf63, 0x2502, 0x4cda, \
|
||||||
|
0xbc, 0xfa, 0x24, 0x65, 0xd2, 0xb0, 0xfe, 0x9d)
|
||||||
#define EFI_CERT_TYPE_PKCS7_GUID \
|
#define EFI_CERT_TYPE_PKCS7_GUID \
|
||||||
EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
|
EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
|
||||||
0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
|
0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
|
||||||
|
|
|
@ -300,6 +300,8 @@ extern const efi_guid_t efi_guid_image_security_database;
|
||||||
extern const efi_guid_t efi_guid_sha256;
|
extern const efi_guid_t efi_guid_sha256;
|
||||||
extern const efi_guid_t efi_guid_cert_x509;
|
extern const efi_guid_t efi_guid_cert_x509;
|
||||||
extern const efi_guid_t efi_guid_cert_x509_sha256;
|
extern const efi_guid_t efi_guid_cert_x509_sha256;
|
||||||
|
extern const efi_guid_t efi_guid_cert_x509_sha384;
|
||||||
|
extern const efi_guid_t efi_guid_cert_x509_sha512;
|
||||||
extern const efi_guid_t efi_guid_cert_type_pkcs7;
|
extern const efi_guid_t efi_guid_cert_type_pkcs7;
|
||||||
|
|
||||||
/* GUID of RNG protocol */
|
/* GUID of RNG protocol */
|
||||||
|
@ -677,6 +679,10 @@ efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size);
|
||||||
/* get a device path from a Boot#### option */
|
/* get a device path from a Boot#### option */
|
||||||
struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid);
|
struct efi_device_path *efi_get_dp_from_boot(const efi_guid_t guid);
|
||||||
|
|
||||||
|
/* get len, string (used in u-boot crypto from a guid */
|
||||||
|
const char *guid_to_sha_str(const efi_guid_t *guid);
|
||||||
|
int algo_to_len(const char *algo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efi_size_in_pages() - convert size in bytes to size in pages
|
* efi_size_in_pages() - convert size in bytes to size in pages
|
||||||
*
|
*
|
||||||
|
|
|
@ -92,3 +92,69 @@ err:
|
||||||
free(var_value);
|
free(var_value);
|
||||||
return NULL;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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_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 = 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_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;
|
const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
|
||||||
|
|
||||||
static u8 pkcs7_hdr[] = {
|
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
|
* Return: true on success, false on error
|
||||||
*/
|
*/
|
||||||
static bool efi_hash_regions(struct image_region *regs, int count,
|
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) {
|
if (!*hash) {
|
||||||
*hash = calloc(1, SHA256_SUM_LEN);
|
*hash = calloc(1, hash_len);
|
||||||
if (!*hash) {
|
if (!*hash) {
|
||||||
EFI_PRINT("Out of memory\n");
|
EFI_PRINT("Out of memory\n");
|
||||||
return false;
|
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
|
#ifdef DEBUG
|
||||||
EFI_PRINT("hash calculated:\n");
|
EFI_PRINT("hash calculated:\n");
|
||||||
print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
|
print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
|
||||||
*hash, SHA256_SUM_LEN, false);
|
*hash, hash_len, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -190,7 +204,6 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs,
|
||||||
struct efi_signature_store *siglist;
|
struct efi_signature_store *siglist;
|
||||||
struct efi_sig_data *sig_data;
|
struct efi_sig_data *sig_data;
|
||||||
void *hash = NULL;
|
void *hash = NULL;
|
||||||
size_t size = 0;
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
bool hash_done = false;
|
bool hash_done = false;
|
||||||
|
|
||||||
|
@ -200,6 +213,8 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
for (siglist = db; siglist; siglist = siglist->next) {
|
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
|
* if the hash algorithm is unsupported and we get an entry in
|
||||||
* dbx reject the image
|
* 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))
|
if (guidcmp(&siglist->sig_type, &efi_guid_sha256))
|
||||||
continue;
|
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 &&
|
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");
|
EFI_PRINT("Digesting an image failed\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -229,8 +250,8 @@ bool efi_signature_lookup_digest(struct efi_image_regions *regs,
|
||||||
print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
|
print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
|
||||||
sig_data->data, sig_data->size, false);
|
sig_data->data, sig_data->size, false);
|
||||||
#endif
|
#endif
|
||||||
if (sig_data->size == size &&
|
if (sig_data->size == len &&
|
||||||
!memcmp(sig_data->data, hash, size)) {
|
!memcmp(sig_data->data, hash, len)) {
|
||||||
found = true;
|
found = true;
|
||||||
free(hash);
|
free(hash);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -263,8 +284,9 @@ static bool efi_lookup_certificate(struct x509_certificate *cert,
|
||||||
struct efi_sig_data *sig_data;
|
struct efi_sig_data *sig_data;
|
||||||
struct image_region reg[1];
|
struct image_region reg[1];
|
||||||
void *hash = NULL, *hash_tmp = NULL;
|
void *hash = NULL, *hash_tmp = NULL;
|
||||||
size_t size = 0;
|
int len = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
const char *hash_algo = NULL;
|
||||||
|
|
||||||
EFI_PRINT("%s: Enter, %p, %p\n", __func__, cert, db);
|
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 */
|
/* calculate hash of TBSCertificate */
|
||||||
reg[0].data = cert->tbs;
|
reg[0].data = cert->tbs;
|
||||||
reg[0].size = cert->tbs_size;
|
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;
|
goto out;
|
||||||
|
|
||||||
EFI_PRINT("%s: searching for %s\n", __func__, cert->subject);
|
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);
|
cert_tmp->subject);
|
||||||
reg[0].data = cert_tmp->tbs;
|
reg[0].data = cert_tmp->tbs;
|
||||||
reg[0].size = cert_tmp->tbs_size;
|
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;
|
goto out;
|
||||||
|
|
||||||
x509_free_certificate(cert_tmp);
|
x509_free_certificate(cert_tmp);
|
||||||
|
|
||||||
if (!memcmp(hash, hash_tmp, size)) {
|
if (!memcmp(hash, hash_tmp, len)) {
|
||||||
found = true;
|
found = true;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -400,9 +426,10 @@ static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo,
|
||||||
struct efi_sig_data *sig_data;
|
struct efi_sig_data *sig_data;
|
||||||
struct image_region reg[1];
|
struct image_region reg[1];
|
||||||
void *hash = NULL;
|
void *hash = NULL;
|
||||||
size_t size = 0;
|
int len = 0;
|
||||||
time64_t revoc_time;
|
time64_t revoc_time;
|
||||||
bool revoked = false;
|
bool revoked = false;
|
||||||
|
const char *hash_algo = NULL;
|
||||||
|
|
||||||
EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx);
|
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);
|
EFI_PRINT("Checking revocation against %s\n", cert->subject);
|
||||||
for (siglist = dbx; siglist; siglist = siglist->next) {
|
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;
|
continue;
|
||||||
|
|
||||||
/* calculate hash of TBSCertificate */
|
/* calculate hash of TBSCertificate */
|
||||||
reg[0].data = cert->tbs;
|
reg[0].data = cert->tbs;
|
||||||
reg[0].size = cert->tbs_size;
|
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;
|
goto out;
|
||||||
|
|
||||||
for (sig_data = siglist->sig_data_list; sig_data;
|
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
|
#ifdef DEBUG
|
||||||
if (sig_data->size >= size) {
|
if (sig_data->size >= len) {
|
||||||
EFI_PRINT("hash in db:\n");
|
EFI_PRINT("hash in db:\n");
|
||||||
print_hex_dump(" ", DUMP_PREFIX_OFFSET,
|
print_hex_dump(" ", DUMP_PREFIX_OFFSET,
|
||||||
16, 1,
|
16, 1,
|
||||||
sig_data->data, size, false);
|
sig_data->data, len, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if ((sig_data->size < size + sizeof(time64_t)) ||
|
if ((sig_data->size < len + sizeof(time64_t)) ||
|
||||||
memcmp(sig_data->data, hash, size))
|
memcmp(sig_data->data, hash, len))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
memcpy(&revoc_time, sig_data->data + size,
|
memcpy(&revoc_time, sig_data->data + len,
|
||||||
sizeof(revoc_time));
|
sizeof(revoc_time));
|
||||||
EFI_PRINT("revocation time: 0x%llx\n", 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 &&
|
if (!msg->data &&
|
||||||
!efi_hash_regions(regs->reg, regs->num,
|
!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");
|
EFI_PRINT("Digesting an image failed\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue