efi_loader: add sha384/512 on certificate revocation
authorIlias Apalodimas <ilias.apalodimas@linaro.org>
Fri, 6 May 2022 12:36:00 +0000 (15:36 +0300)
committerHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sat, 7 May 2022 21:17:26 +0000 (23:17 +0200)
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>
include/efi_api.h
include/efi_loader.h
lib/efi_loader/efi_helper.c
lib/efi_loader/efi_signature.c

index c7f7873..83c0108 100644 (file)
@@ -1873,6 +1873,12 @@ struct efi_system_resource_table {
 #define EFI_CERT_X509_SHA256_GUID \
        EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
                 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 \
        EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
                 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
index effb433..733ee03 100644 (file)
@@ -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_cert_x509;
 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;
 
 /* 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 */
 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
  *
index 802d39e..c4499f6 100644 (file)
@@ -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;
+}
index 79ed077..ddac751 100644 (file)
@@ -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;
                }