| /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| * | |
| * Misc functions which need access to vb2_context but are not public APIs | |
| */ | |
| #include "2sysincludes.h" | |
| #include "2api.h" | |
| #include "2common.h" | |
| #include "2misc.h" | |
| #include "2nvstorage.h" | |
| #include "2secdata.h" | |
| #include "2sha.h" | |
| #include "2rsa.h" | |
| #include "vb21_common.h" | |
| /** | |
| * Read an object with a common struct header from a verified boot resource. | |
| * | |
| * On success, an object buffer will be allocated in the work buffer, the | |
| * object will be stored into the buffer, and *buf_ptr will point to the | |
| * object. | |
| * | |
| * @param ctx Vboot context | |
| * @param index Resource index to read | |
| * @param offset Byte offset within resource to start at | |
| * @param buf_ptr Destination for object pointer | |
| * @return VB2_SUCCESS, or error code on error. | |
| */ | |
| static int vb21_read_resource_object(struct vb2_context *ctx, | |
| enum vb2_resource_index index, | |
| uint32_t offset, | |
| struct vb2_workbuf *wb, | |
| void **buf_ptr) | |
| { | |
| struct vb21_struct_common c; | |
| void *buf; | |
| int rv; | |
| *buf_ptr = NULL; | |
| /* Read the common header */ | |
| rv = vb2ex_read_resource(ctx, index, offset, &c, sizeof(c)); | |
| if (rv) | |
| return rv; | |
| /* Allocate a buffer for the object, now that we know how big it is */ | |
| buf = vb2_workbuf_alloc(wb, c.total_size); | |
| if (!buf) | |
| return VB2_ERROR_READ_RESOURCE_OBJECT_BUF; | |
| /* Read the object */ | |
| rv = vb2ex_read_resource(ctx, index, offset, buf, c.total_size); | |
| if (rv) { | |
| vb2_workbuf_free(wb, c.total_size); | |
| return rv; | |
| } | |
| /* Save the pointer */ | |
| *buf_ptr = buf; | |
| return VB2_SUCCESS; | |
| } | |
| int vb21_load_fw_keyblock(struct vb2_context *ctx) | |
| { | |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); | |
| struct vb2_workbuf wb; | |
| uint8_t *key_data; | |
| uint32_t key_size; | |
| struct vb21_packed_key *packed_key; | |
| struct vb2_public_key root_key; | |
| struct vb21_keyblock *kb; | |
| int rv; | |
| vb2_workbuf_from_ctx(ctx, &wb); | |
| /* Read the root key */ | |
| key_size = sd->gbb_rootkey_size; | |
| key_data = vb2_workbuf_alloc(&wb, key_size); | |
| if (!key_data) | |
| return VB2_ERROR_FW_KEYBLOCK_WORKBUF_ROOT_KEY; | |
| rv = vb2ex_read_resource(ctx, VB2_RES_GBB, sd->gbb_rootkey_offset, | |
| key_data, key_size); | |
| if (rv) | |
| return rv; | |
| /* Unpack the root key */ | |
| rv = vb21_unpack_key(&root_key, key_data, key_size); | |
| if (rv) | |
| return rv; | |
| /* | |
| * Load the firmware keyblock common header into the work buffer after | |
| * the root key. | |
| */ | |
| rv = vb21_read_resource_object(ctx, VB2_RES_FW_VBLOCK, 0, &wb, | |
| (void **)&kb); | |
| if (rv) | |
| return rv; | |
| /* Verify the keyblock */ | |
| rv = vb21_verify_keyblock(kb, kb->c.total_size, &root_key, &wb); | |
| if (rv) { | |
| vb2_fail(ctx, VB2_RECOVERY_FW_KEYBLOCK, rv); | |
| return rv; | |
| } | |
| /* Preamble follows the keyblock in the vblock */ | |
| sd->vblock_preamble_offset = kb->c.total_size; | |
| packed_key = (struct vb21_packed_key *)((uint8_t *)kb + kb->key_offset); | |
| /* Key version is the upper 16 bits of the composite firmware version */ | |
| if (packed_key->key_version > 0xffff) | |
| rv = VB2_ERROR_FW_KEYBLOCK_VERSION_RANGE; | |
| if (!rv && packed_key->key_version < (sd->fw_version_secdata >> 16)) { | |
| if (sd->gbb_flags & VB2_GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK) | |
| VB2_DEBUG("Ignoring FW key rollback due to GBB flag\n"); | |
| else | |
| rv = VB2_ERROR_FW_KEYBLOCK_VERSION_ROLLBACK; | |
| } | |
| if (rv) { | |
| vb2_fail(ctx, VB2_RECOVERY_FW_KEY_ROLLBACK, rv); | |
| return rv; | |
| } | |
| sd->fw_version = packed_key->key_version << 16; | |
| /* | |
| * Save the data key in the work buffer. This overwrites the root key | |
| * we read above. That's ok, because now that we have the data key we | |
| * no longer need the root key. | |
| * | |
| * Use memmove() instead of memcpy(). In theory, the destination will | |
| * never overlap with the source because the root key is likely to be | |
| * at least as large as the data key, but there's no harm here in being | |
| * paranoid. | |
| */ | |
| memmove(key_data, packed_key, packed_key->c.total_size); | |
| packed_key = (struct vb21_packed_key *)key_data; | |
| /* Save the packed key offset and size */ | |
| sd->workbuf_data_key_offset = vb2_offset_of(ctx->workbuf, key_data); | |
| sd->workbuf_data_key_size = packed_key->c.total_size; | |
| /* Data key will persist in the workbuf after we return */ | |
| vb2_set_workbuf_used(ctx, sd->workbuf_data_key_offset + | |
| sd->workbuf_data_key_size); | |
| return VB2_SUCCESS; | |
| } | |
| int vb21_load_fw_preamble(struct vb2_context *ctx) | |
| { | |
| struct vb2_shared_data *sd = vb2_get_sd(ctx); | |
| struct vb2_workbuf wb; | |
| uint8_t *key_data = ctx->workbuf + sd->workbuf_data_key_offset; | |
| uint32_t key_size = sd->workbuf_data_key_size; | |
| struct vb2_public_key data_key; | |
| /* Preamble goes in the next unused chunk of work buffer */ | |
| struct vb21_fw_preamble *pre; | |
| int rv; | |
| vb2_workbuf_from_ctx(ctx, &wb); | |
| /* Unpack the firmware data key */ | |
| if (!sd->workbuf_data_key_size) | |
| return VB2_ERROR_FW_PREAMBLE2_DATA_KEY; | |
| rv = vb21_unpack_key(&data_key, key_data, key_size); | |
| if (rv) | |
| return rv; | |
| /* Load the firmware preamble */ | |
| rv = vb21_read_resource_object(ctx, VB2_RES_FW_VBLOCK, | |
| sd->vblock_preamble_offset, &wb, | |
| (void **)&pre); | |
| if (rv) | |
| return rv; | |
| /* Work buffer now contains the data subkey data and the preamble */ | |
| /* Verify the preamble */ | |
| rv = vb21_verify_fw_preamble(pre, pre->c.total_size, &data_key, &wb); | |
| if (rv) { | |
| vb2_fail(ctx, VB2_RECOVERY_FW_PREAMBLE, rv); | |
| return rv; | |
| } | |
| /* Move the preamble down now that the data key is no longer used */ | |
| memmove(key_data, pre, pre->c.total_size); | |
| pre = (struct vb21_fw_preamble *)key_data; | |
| /* Data key is now gone */ | |
| sd->workbuf_data_key_offset = sd->workbuf_data_key_size = 0; | |
| /* | |
| * Firmware version is the lower 16 bits of the composite firmware | |
| * version. | |
| */ | |
| if (pre->fw_version > 0xffff) | |
| rv = VB2_ERROR_FW_PREAMBLE_VERSION_RANGE; | |
| /* Combine with the key version from vb2_load_fw_keyblock() */ | |
| sd->fw_version |= pre->fw_version; | |
| if (!rv && sd->fw_version < sd->fw_version_secdata) { | |
| if (sd->gbb_flags & VB2_GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK) | |
| VB2_DEBUG("Ignoring FW rollback due to GBB flag\n"); | |
| else | |
| rv = VB2_ERROR_FW_PREAMBLE_VERSION_ROLLBACK; | |
| } | |
| if (rv) { | |
| vb2_fail(ctx, VB2_RECOVERY_FW_ROLLBACK, rv); | |
| return rv; | |
| } | |
| /* | |
| * If this is a newer version than in secure storage, and we | |
| * successfully booted the same slot last boot, roll forward the | |
| * version in secure storage. | |
| */ | |
| if (sd->fw_version > sd->fw_version_secdata && | |
| sd->last_fw_slot == sd->fw_slot && | |
| sd->last_fw_result == VB2_FW_RESULT_SUCCESS) { | |
| sd->fw_version_secdata = sd->fw_version; | |
| rv = vb2_secdata_set(ctx, VB2_SECDATA_VERSIONS, sd->fw_version); | |
| if (rv) | |
| return rv; | |
| } | |
| /* Keep track of where we put the preamble */ | |
| sd->workbuf_preamble_offset = vb2_offset_of(ctx->workbuf, pre); | |
| sd->workbuf_preamble_size = pre->c.total_size; | |
| /* Preamble will persist in work buffer after we return */ | |
| vb2_set_workbuf_used(ctx, sd->workbuf_preamble_offset + | |
| sd->workbuf_preamble_size); | |
| return VB2_SUCCESS; | |
| } |