| // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| /* Copyright 2020 IBM Corp. */ |
| #ifndef pr_fmt |
| #define pr_fmt(fmt) "EDK2_COMPAT: " fmt |
| #endif |
| |
| #include <opal.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <stdint.h> |
| #include <ccan/endian/endian.h> |
| #include <mbedtls/error.h> |
| #include <mbedtls/oid.h> |
| #include <device.h> |
| #include <assert.h> |
| #include "libstb/crypto/pkcs7/pkcs7.h" |
| #include "edk2.h" |
| #include "../secvar.h" |
| #include "edk2-compat-process.h" |
| |
| bool setup_mode; |
| |
| int update_variable_in_bank(struct secvar *update_var, const char *data, |
| const uint64_t dsize, struct list_head *bank) |
| { |
| struct secvar *var; |
| |
| var = find_secvar(update_var->key, update_var->key_len, bank); |
| if (!var) |
| return OPAL_EMPTY; |
| |
| /* Reallocate the data memory, if there is change in data size */ |
| if (var->data_size < dsize) |
| if (realloc_secvar(var, dsize)) |
| return OPAL_NO_MEM; |
| |
| if (dsize && data) |
| memcpy(var->data, data, dsize); |
| var->data_size = dsize; |
| |
| /* Clear the volatile bit only if updated with positive data size */ |
| if (dsize) |
| var->flags &= ~SECVAR_FLAG_VOLATILE; |
| else |
| var->flags |= SECVAR_FLAG_VOLATILE; |
| |
| if (key_equals(update_var->key, "PK") |
| || key_equals(update_var->key, "HWKH") |
| || key_equals(update_var->key, "TS")) |
| var->flags |= SECVAR_FLAG_PROTECTED; |
| |
| return 0; |
| } |
| |
| /* Expand char to wide character size */ |
| static char *char_to_wchar(const char *key, const size_t keylen) |
| { |
| int i; |
| char *str; |
| |
| str = zalloc(keylen * 2); |
| if (!str) |
| return NULL; |
| |
| for (i = 0; i < keylen*2; key++) { |
| str[i++] = *key; |
| str[i++] = '\0'; |
| } |
| |
| return str; |
| } |
| |
| /* Returns the authority that can sign the given key update */ |
| static void get_key_authority(const char *ret[3], const char *key) |
| { |
| int i = 0; |
| |
| if (key_equals(key, "PK")) { |
| ret[i++] = "PK"; |
| } else if (key_equals(key, "KEK")) { |
| ret[i++] = "PK"; |
| } else if (key_equals(key, "db") || key_equals(key, "dbx")) { |
| ret[i++] = "KEK"; |
| ret[i++] = "PK"; |
| } |
| |
| ret[i] = NULL; |
| } |
| |
| static EFI_SIGNATURE_LIST* get_esl_signature_list(const char *buf, size_t buflen) |
| { |
| EFI_SIGNATURE_LIST *list = NULL; |
| |
| if (buflen < sizeof(EFI_SIGNATURE_LIST) || !buf) |
| return NULL; |
| |
| list = (EFI_SIGNATURE_LIST *)buf; |
| |
| return list; |
| } |
| |
| /* Returns the size of the complete ESL. */ |
| static int32_t get_esl_signature_list_size(const char *buf, const size_t buflen) |
| { |
| EFI_SIGNATURE_LIST *list = get_esl_signature_list(buf, buflen); |
| |
| if (!list) |
| return OPAL_PARAMETER; |
| |
| return le32_to_cpu(list->SignatureListSize); |
| } |
| |
| /* |
| * Copies the certificate from the ESL into cert buffer and returns the size |
| * of the certificate |
| */ |
| static int get_esl_cert(const char *buf, const size_t buflen, char **cert) |
| { |
| size_t sig_data_offset; |
| size_t size; |
| EFI_SIGNATURE_LIST *list = get_esl_signature_list(buf, buflen); |
| |
| if (!list) |
| return OPAL_PARAMETER; |
| |
| assert(cert != NULL); |
| |
| if (le32_to_cpu(list->SignatureSize) <= sizeof(uuid_t)) |
| return OPAL_PARAMETER; |
| |
| size = le32_to_cpu(list->SignatureSize) - sizeof(uuid_t); |
| |
| prlog(PR_DEBUG,"size of signature list size is %u\n", |
| le32_to_cpu(list->SignatureListSize)); |
| prlog(PR_DEBUG, "size of signature header size is %u\n", |
| le32_to_cpu(list->SignatureHeaderSize)); |
| prlog(PR_DEBUG, "size of signature size is %u\n", |
| le32_to_cpu(list->SignatureSize)); |
| |
| sig_data_offset = sizeof(EFI_SIGNATURE_LIST) |
| + le32_to_cpu(list->SignatureHeaderSize) |
| + 16 * sizeof(uint8_t); |
| |
| /* Ensure this ESL does not overflow the bounds of the buffer */ |
| if (sig_data_offset + size > buflen) { |
| prlog(PR_ERR, "Number of bytes of ESL data is less than size specified\n"); |
| return OPAL_PARAMETER; |
| } |
| |
| *cert = zalloc(size); |
| if (!(*cert)) |
| return OPAL_NO_MEM; |
| |
| /* Since buf can have more than one ESL, copy only the size calculated |
| * to return single ESL */ |
| memcpy(*cert, buf + sig_data_offset, size); |
| |
| return size; |
| } |
| |
| /* |
| * Extracts size of the PKCS7 signed data embedded in the |
| * struct Authentication 2 Descriptor Header. |
| */ |
| static size_t get_pkcs7_len(const struct efi_variable_authentication_2 *auth) |
| { |
| uint32_t dw_length; |
| size_t size; |
| |
| assert(auth != NULL); |
| |
| dw_length = le32_to_cpu(auth->auth_info.hdr.dw_length); |
| size = dw_length - (sizeof(auth->auth_info.hdr.dw_length) |
| + sizeof(auth->auth_info.hdr.w_revision) |
| + sizeof(auth->auth_info.hdr.w_certificate_type) |
| + sizeof(auth->auth_info.cert_type)); |
| |
| return size; |
| } |
| |
| int get_auth_descriptor2(const void *buf, const size_t buflen, void **auth_buffer) |
| { |
| const struct efi_variable_authentication_2 *auth = buf; |
| int auth_buffer_size; |
| size_t len; |
| |
| assert(auth_buffer != NULL); |
| if (buflen < sizeof(struct efi_variable_authentication_2) |
| || !buf) |
| return OPAL_PARAMETER; |
| |
| len = get_pkcs7_len(auth); |
| /* pkcs7 content length cannot be greater than buflen */ |
| if (len > buflen) |
| return OPAL_PARAMETER; |
| |
| auth_buffer_size = sizeof(auth->timestamp) + sizeof(auth->auth_info.hdr) |
| + sizeof(auth->auth_info.cert_type) + len; |
| |
| if (auth_buffer_size > buflen) |
| return OPAL_PARAMETER; |
| |
| *auth_buffer = zalloc(auth_buffer_size); |
| if (!(*auth_buffer)) |
| return OPAL_NO_MEM; |
| |
| /* |
| * Data = auth descriptor + new ESL data. |
| * Extracts only the auth descriptor from data. |
| */ |
| memcpy(*auth_buffer, buf, auth_buffer_size); |
| |
| return auth_buffer_size; |
| } |
| |
| static bool validate_cert(char *signing_cert, int signing_cert_size) |
| { |
| mbedtls_x509_crt x509; |
| char *x509_buf = NULL; |
| int rc; |
| |
| mbedtls_x509_crt_init(&x509); |
| rc = mbedtls_x509_crt_parse(&x509, signing_cert, signing_cert_size); |
| |
| /* If failure in parsing the certificate, exit */ |
| if(rc) { |
| prlog(PR_ERR, "X509 certificate parsing failed %04x\n", rc); |
| return false; |
| } |
| |
| x509_buf = zalloc(CERT_BUFFER_SIZE); |
| rc = mbedtls_x509_crt_info(x509_buf, CERT_BUFFER_SIZE, "CRT:", &x509); |
| |
| mbedtls_x509_crt_free(&x509); |
| free(x509_buf); |
| x509_buf = NULL; |
| |
| /* If failure in reading the certificate, exit */ |
| if (rc < 0) |
| return false; |
| |
| return true; |
| } |
| |
| static bool validate_hash(uuid_t type, int size) |
| { |
| if (uuid_equals(&type, &EFI_CERT_SHA1_GUID) && (size == 20)) |
| return true; |
| |
| if (uuid_equals(&type, &EFI_CERT_SHA224_GUID) && (size == 28)) |
| return true; |
| |
| if (uuid_equals(&type, &EFI_CERT_SHA256_GUID) && (size == 32)) |
| return true; |
| |
| if (uuid_equals(&type, &EFI_CERT_SHA384_GUID) && (size == 48)) |
| return true; |
| |
| if (uuid_equals(&type, &EFI_CERT_SHA512_GUID) && (size == 64)) |
| return true; |
| |
| return false; |
| } |
| |
| int validate_esl_list(const char *key, const char *esl, const size_t size) |
| { |
| int count = 0; |
| int dsize; |
| char *data = NULL; |
| int eslvarsize = size; |
| int eslsize; |
| int rc = OPAL_SUCCESS; |
| EFI_SIGNATURE_LIST *list = NULL; |
| |
| while (eslvarsize > 0) { |
| prlog(PR_DEBUG, "esl var size is %d offset is %lu\n", eslvarsize, size - eslvarsize); |
| if (eslvarsize < sizeof(EFI_SIGNATURE_LIST)) { |
| prlog(PR_ERR, "ESL with size %d is too small\n", eslvarsize); |
| rc = OPAL_PARAMETER; |
| break; |
| } |
| |
| /* Check Supported ESL Type */ |
| list = get_esl_signature_list(esl, eslvarsize); |
| |
| if (!list) |
| return OPAL_PARAMETER; |
| |
| /* Calculate the size of the ESL */ |
| eslsize = le32_to_cpu(list->SignatureListSize); |
| |
| /* If could not extract the size */ |
| if (eslsize <= 0) { |
| prlog(PR_ERR, "Invalid size of the ESL: %u\n", |
| le32_to_cpu(list->SignatureListSize)); |
| rc = OPAL_PARAMETER; |
| break; |
| } |
| |
| /* Extract the certificate from the ESL */ |
| dsize = get_esl_cert(esl, eslvarsize, &data); |
| if (dsize < 0) { |
| rc = dsize; |
| break; |
| } |
| |
| if (key_equals(key, "dbx")) { |
| if (!validate_hash(list->SignatureType, dsize)) { |
| prlog(PR_ERR, "No valid hash is found\n"); |
| rc = OPAL_PARAMETER; |
| break; |
| } |
| } else { |
| if (!uuid_equals(&list->SignatureType, &EFI_CERT_X509_GUID) |
| || !validate_cert(data, dsize)) { |
| prlog(PR_ERR, "No valid cert is found\n"); |
| rc = OPAL_PARAMETER; |
| break; |
| } |
| } |
| |
| count++; |
| |
| /* Look for the next ESL */ |
| esl = esl + eslsize; |
| eslvarsize = eslvarsize - eslsize; |
| free(data); |
| /* Since we are going to allocate again in the next iteration */ |
| data = NULL; |
| } |
| |
| if (rc == OPAL_SUCCESS) { |
| if (key_equals(key, "PK") && (count > 1)) { |
| prlog(PR_ERR, "PK can only be one\n"); |
| rc = OPAL_PARAMETER; |
| } else { |
| rc = count; |
| } |
| } |
| |
| free(data); |
| |
| prlog(PR_INFO, "Total ESLs are %d\n", rc); |
| return rc; |
| } |
| |
| /* Get the timestamp for the last update of the give key */ |
| static struct efi_time *get_last_timestamp(const char *key, char *last_timestamp) |
| { |
| struct efi_time *timestamp = (struct efi_time*)last_timestamp; |
| |
| if (!last_timestamp) |
| return NULL; |
| |
| if (key_equals(key, "PK")) |
| return ×tamp[0]; |
| else if (key_equals(key, "KEK")) |
| return ×tamp[1]; |
| else if (key_equals(key, "db")) |
| return ×tamp[2]; |
| else if (key_equals(key, "dbx")) |
| return ×tamp[3]; |
| else |
| return NULL; |
| } |
| |
| int update_timestamp(const char *key, const struct efi_time *timestamp, char *last_timestamp) |
| { |
| struct efi_time *prev; |
| |
| prev = get_last_timestamp(key, last_timestamp); |
| if (prev == NULL) |
| return OPAL_INTERNAL_ERROR; |
| |
| /* Update with new timestamp */ |
| memcpy(prev, timestamp, sizeof(struct efi_time)); |
| |
| prlog(PR_DEBUG, "updated prev year is %d month %d day %d\n", |
| le16_to_cpu(prev->year), prev->month, prev->day); |
| |
| return OPAL_SUCCESS; |
| } |
| |
| static uint64_t unpack_timestamp(const struct efi_time *timestamp) |
| { |
| uint64_t val = 0; |
| uint16_t year = le16_to_cpu(timestamp->year); |
| |
| /* pad1, nanosecond, timezone, daylight and pad2 are meant to be zero */ |
| val |= ((uint64_t) timestamp->pad1 & 0xFF) << 0; |
| val |= ((uint64_t) timestamp->second & 0xFF) << (1*8); |
| val |= ((uint64_t) timestamp->minute & 0xFF) << (2*8); |
| val |= ((uint64_t) timestamp->hour & 0xFF) << (3*8); |
| val |= ((uint64_t) timestamp->day & 0xFF) << (4*8); |
| val |= ((uint64_t) timestamp->month & 0xFF) << (5*8); |
| val |= ((uint64_t) year) << (6*8); |
| |
| return val; |
| } |
| |
| int check_timestamp(const char *key, const struct efi_time *timestamp, |
| char *last_timestamp) |
| { |
| struct efi_time *prev; |
| uint64_t new; |
| uint64_t last; |
| |
| prev = get_last_timestamp(key, last_timestamp); |
| if (prev == NULL) |
| return OPAL_INTERNAL_ERROR; |
| |
| prlog(PR_DEBUG, "timestamp year is %d month %d day %d\n", |
| le16_to_cpu(timestamp->year), timestamp->month, |
| timestamp->day); |
| prlog(PR_DEBUG, "prev year is %d month %d day %d\n", |
| le16_to_cpu(prev->year), prev->month, prev->day); |
| |
| new = unpack_timestamp(timestamp); |
| last = unpack_timestamp(prev); |
| |
| if (new > last) |
| return OPAL_SUCCESS; |
| |
| return OPAL_PERMISSION; |
| } |
| |
| /* Extract PKCS7 from the authentication header */ |
| static mbedtls_pkcs7* get_pkcs7(const struct efi_variable_authentication_2 *auth) |
| { |
| char *checkpkcs7cert = NULL; |
| size_t len; |
| mbedtls_pkcs7 *pkcs7 = NULL; |
| int rc; |
| |
| len = get_pkcs7_len(auth); |
| |
| pkcs7 = malloc(sizeof(struct mbedtls_pkcs7)); |
| if (!pkcs7) |
| return NULL; |
| |
| mbedtls_pkcs7_init(pkcs7); |
| rc = mbedtls_pkcs7_parse_der( auth->auth_info.cert_data, len, pkcs7); |
| if (rc <= 0) { |
| prlog(PR_ERR, "Parsing pkcs7 failed %04x\n", rc); |
| goto out; |
| } |
| |
| checkpkcs7cert = zalloc(CERT_BUFFER_SIZE); |
| if (!checkpkcs7cert) |
| goto out; |
| |
| rc = mbedtls_x509_crt_info(checkpkcs7cert, CERT_BUFFER_SIZE, "CRT:", |
| &(pkcs7->signed_data.certs)); |
| if (rc < 0) { |
| prlog(PR_ERR, "Failed to parse the certificate in PKCS7 structure\n"); |
| free(checkpkcs7cert); |
| goto out; |
| } |
| |
| prlog(PR_DEBUG, "%s \n", checkpkcs7cert); |
| free(checkpkcs7cert); |
| return pkcs7; |
| |
| out: |
| mbedtls_pkcs7_free(pkcs7); |
| free(pkcs7); |
| pkcs7 = NULL; |
| return pkcs7; |
| } |
| |
| /* Verify the PKCS7 signature on the signed data. */ |
| static int verify_signature(const struct efi_variable_authentication_2 *auth, |
| const char *hash, const size_t hash_len, |
| const struct secvar *avar) |
| { |
| mbedtls_pkcs7 *pkcs7 = NULL; |
| mbedtls_x509_crt x509; |
| mbedtls_md_type_t md_alg; |
| char *signing_cert = NULL; |
| char *x509_buf = NULL; |
| int signing_cert_size; |
| int rc = 0; |
| char *errbuf; |
| int eslvarsize; |
| int eslsize; |
| int offset = 0; |
| |
| if (!auth) |
| return OPAL_PARAMETER; |
| |
| /* Extract the pkcs7 from the auth structure */ |
| pkcs7 = get_pkcs7(auth); |
| /* Failure to parse pkcs7 implies bad input. */ |
| if (!pkcs7) |
| return OPAL_PARAMETER; |
| |
| /* |
| * We only support sha256, which has a hash length of 32. |
| * If the alg is not sha256, then we should bail now. |
| */ |
| rc = mbedtls_oid_get_md_alg(&pkcs7->signed_data.digest_alg_identifiers, |
| &md_alg); |
| if (rc != 0) { |
| prlog(PR_ERR, "Failed to get the Digest Algorithm Identifier: %d\n", rc); |
| rc = OPAL_PARAMETER; |
| goto err_pkcs7; |
| } |
| |
| if (md_alg != MBEDTLS_MD_SHA256) { |
| prlog(PR_ERR, "Unexpected digest algorithm: expected %d (SHA-256), got %d\n", |
| MBEDTLS_MD_SHA256, md_alg); |
| rc = OPAL_PARAMETER; |
| goto err_pkcs7; |
| } |
| |
| prlog(PR_INFO, "Load the signing certificate from the keystore"); |
| |
| eslvarsize = avar->data_size; |
| |
| /* Variable is not empty */ |
| while (eslvarsize > 0) { |
| prlog(PR_DEBUG, "esl var size is %d offset is %d\n", eslvarsize, offset); |
| if (eslvarsize < sizeof(EFI_SIGNATURE_LIST)) { |
| rc = OPAL_INTERNAL_ERROR; |
| prlog(PR_ERR, "ESL data is corrupted\n"); |
| break; |
| } |
| |
| /* Calculate the size of the ESL */ |
| eslsize = get_esl_signature_list_size(avar->data + offset, |
| eslvarsize); |
| /* If could not extract the size */ |
| if (eslsize <= 0) { |
| rc = OPAL_PARAMETER; |
| break; |
| } |
| |
| /* Extract the certificate from the ESL */ |
| signing_cert_size = get_esl_cert(avar->data + offset, |
| eslvarsize, &signing_cert); |
| if (signing_cert_size < 0) { |
| rc = signing_cert_size; |
| break; |
| } |
| |
| mbedtls_x509_crt_init(&x509); |
| rc = mbedtls_x509_crt_parse(&x509, |
| signing_cert, |
| signing_cert_size); |
| |
| /* This should not happen, unless something corrupted in PNOR */ |
| if(rc) { |
| prlog(PR_ERR, "X509 certificate parsing failed %04x\n", rc); |
| rc = OPAL_INTERNAL_ERROR; |
| break; |
| } |
| |
| x509_buf = zalloc(CERT_BUFFER_SIZE); |
| rc = mbedtls_x509_crt_info(x509_buf, |
| CERT_BUFFER_SIZE, |
| "CRT:", |
| &x509); |
| |
| /* This should not happen, unless something corrupted in PNOR */ |
| if (rc < 0) { |
| free(x509_buf); |
| rc = OPAL_INTERNAL_ERROR; |
| break; |
| } |
| |
| prlog(PR_INFO, "%s \n", x509_buf); |
| free(x509_buf); |
| x509_buf = NULL; |
| |
| rc = mbedtls_pkcs7_signed_hash_verify(pkcs7, &x509, hash, hash_len); |
| |
| /* If you find a signing certificate, you are done */ |
| if (rc == 0) { |
| prlog(PR_INFO, "Signature Verification passed\n"); |
| mbedtls_x509_crt_free(&x509); |
| break; |
| } else { |
| errbuf = zalloc(MBEDTLS_ERR_BUFFER_SIZE); |
| mbedtls_strerror(rc, errbuf, MBEDTLS_ERR_BUFFER_SIZE); |
| prlog(PR_ERR, "Signature Verification failed %02x %s\n", |
| rc, errbuf); |
| free(errbuf); |
| rc = OPAL_PERMISSION; |
| } |
| |
| |
| /* Look for the next ESL */ |
| offset = offset + eslsize; |
| eslvarsize = eslvarsize - eslsize; |
| mbedtls_x509_crt_free(&x509); |
| free(signing_cert); |
| /* Since we are going to allocate again in the next iteration */ |
| signing_cert = NULL; |
| |
| } |
| |
| free(signing_cert); |
| err_pkcs7: |
| mbedtls_pkcs7_free(pkcs7); |
| free(pkcs7); |
| |
| return rc; |
| } |
| |
| /* |
| * Create the hash of the buffer |
| * name || vendor guid || attributes || timestamp || newcontent |
| * which is submitted as signed by the user. |
| * Returns the sha256 hash, else NULL. |
| */ |
| static char *get_hash_to_verify(const char *key, const char *new_data, |
| const size_t new_data_size, |
| const struct efi_time *timestamp) |
| { |
| le32 attr = cpu_to_le32(SECVAR_ATTRIBUTES); |
| size_t varlen; |
| char *wkey; |
| uuid_t guid; |
| unsigned char *hash = NULL; |
| const mbedtls_md_info_t *md_info; |
| mbedtls_md_context_t ctx; |
| int rc; |
| |
| md_info = mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ); |
| mbedtls_md_init(&ctx); |
| |
| rc = mbedtls_md_setup(&ctx, md_info, 0); |
| if (rc) |
| goto out; |
| |
| rc = mbedtls_md_starts(&ctx); |
| if (rc) |
| goto out; |
| |
| if (key_equals(key, "PK") |
| || key_equals(key, "KEK")) |
| guid = EFI_GLOBAL_VARIABLE_GUID; |
| else if (key_equals(key, "db") |
| || key_equals(key, "dbx")) |
| guid = EFI_IMAGE_SECURITY_DATABASE_GUID; |
| else |
| goto out; |
| |
| /* Expand char name to wide character width */ |
| varlen = strlen(key) * 2; |
| wkey = char_to_wchar(key, strlen(key)); |
| rc = mbedtls_md_update(&ctx, wkey, varlen); |
| free(wkey); |
| if (rc) |
| goto out; |
| |
| rc = mbedtls_md_update(&ctx, (const unsigned char *)&guid, sizeof(guid)); |
| if (rc) |
| goto out; |
| |
| rc = mbedtls_md_update(&ctx, (const unsigned char *)&attr, sizeof(attr)); |
| if (rc) |
| goto out; |
| |
| rc = mbedtls_md_update(&ctx, (const unsigned char *)timestamp, |
| sizeof(struct efi_time)); |
| if (rc) |
| goto out; |
| |
| rc = mbedtls_md_update(&ctx, new_data, new_data_size); |
| if (rc) |
| goto out; |
| |
| hash = zalloc(32); |
| if (!hash) |
| goto out; |
| rc = mbedtls_md_finish(&ctx, hash); |
| if (rc) { |
| free(hash); |
| hash = NULL; |
| } |
| |
| out: |
| mbedtls_md_free(&ctx); |
| return hash; |
| } |
| |
| bool is_pkcs7_sig_format(const void *data) |
| { |
| const struct efi_variable_authentication_2 *auth = data; |
| uuid_t pkcs7_guid = EFI_CERT_TYPE_PKCS7_GUID; |
| |
| return !memcmp(&auth->auth_info.cert_type, &pkcs7_guid, 16); |
| } |
| |
| int process_update(const struct secvar *update, char **newesl, |
| int *new_data_size, struct efi_time *timestamp, |
| struct list_head *bank, char *last_timestamp) |
| { |
| struct efi_variable_authentication_2 *auth = NULL; |
| void *auth_buffer = NULL; |
| int auth_buffer_size = 0; |
| const char *key_authority[3]; |
| char *hash = NULL; |
| struct secvar *avar = NULL; |
| int rc = 0; |
| int i; |
| |
| /* We need to split data into authentication descriptor and new ESL */ |
| auth_buffer_size = get_auth_descriptor2(update->data, |
| update->data_size, |
| &auth_buffer); |
| if ((auth_buffer_size < 0) |
| || (update->data_size < auth_buffer_size)) { |
| prlog(PR_ERR, "Invalid auth buffer size\n"); |
| rc = auth_buffer_size; |
| goto out; |
| } |
| |
| auth = auth_buffer; |
| |
| if (!timestamp) { |
| rc = OPAL_INTERNAL_ERROR; |
| goto out; |
| } |
| |
| memcpy(timestamp, auth_buffer, sizeof(struct efi_time)); |
| |
| rc = check_timestamp(update->key, timestamp, last_timestamp); |
| /* Failure implies probably an older command being resubmitted */ |
| if (rc != OPAL_SUCCESS) { |
| prlog(PR_ERR, "Timestamp verification failed for key %s\n", update->key); |
| goto out; |
| } |
| |
| /* Calculate the size of new ESL data */ |
| *new_data_size = update->data_size - auth_buffer_size; |
| if (*new_data_size < 0) { |
| prlog(PR_ERR, "Invalid new ESL (new data content) size\n"); |
| rc = OPAL_PARAMETER; |
| goto out; |
| } |
| *newesl = zalloc(*new_data_size); |
| if (!(*newesl)) { |
| rc = OPAL_NO_MEM; |
| goto out; |
| } |
| memcpy(*newesl, update->data + auth_buffer_size, *new_data_size); |
| |
| /* Validate the new ESL is in right format */ |
| rc = validate_esl_list(update->key, *newesl, *new_data_size); |
| if (rc < 0) { |
| prlog(PR_ERR, "ESL validation failed for key %s with error %04x\n", |
| update->key, rc); |
| goto out; |
| } |
| |
| if (setup_mode) { |
| rc = OPAL_SUCCESS; |
| goto out; |
| } |
| |
| /* Prepare the data to be verified */ |
| hash = get_hash_to_verify(update->key, *newesl, *new_data_size, |
| timestamp); |
| if (!hash) { |
| rc = OPAL_INTERNAL_ERROR; |
| goto out; |
| } |
| |
| /* Get the authority to verify the signature */ |
| get_key_authority(key_authority, update->key); |
| |
| /* |
| * Try for all the authorities that are allowed to sign. |
| * For eg. db/dbx can be signed by both PK or KEK |
| */ |
| for (i = 0; key_authority[i] != NULL; i++) { |
| prlog(PR_DEBUG, "key is %s\n", update->key); |
| prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]); |
| avar = find_secvar(key_authority[i], |
| strlen(key_authority[i]) + 1, |
| bank); |
| if (!avar || !avar->data_size) |
| continue; |
| |
| /* Verify the signature. sha256 is 32 bytes long. */ |
| rc = verify_signature(auth, hash, 32, avar); |
| |
| /* Break if signature verification is successful */ |
| if (rc == OPAL_SUCCESS) { |
| prlog(PR_INFO, "Key %s successfully verified by authority %s\n", update->key, key_authority[i]); |
| break; |
| } |
| } |
| |
| out: |
| free(auth_buffer); |
| free(hash); |
| |
| return rc; |
| } |