/* * Copyright 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "avb_manager.h" #include #include #include "secure_storage_interface.h" #define DEBUG 0 const unsigned int kRollbackSlotMax = 32; static const uint32_t kAvbVersion = 1; static const char* kAvbRollbackFilename = "avb.rollback"; static const unsigned int kPermanentAttributesLengthMax = 2048; static const char* kPermanentAttributesFilename = "avb.ppa"; static const char* kLockStateFile = "avb.lock_state"; static const unsigned int kStorageIdLengthMax = 64; static const uint32_t kTypeMask = 0xF000; static const unsigned int kTypeShift = 12; // Helper function to get |filename| and |slot| and based on type flag // in |raw_slot|. |filename| is assumed to be allocated with // kStorageIdLengthMax bytes. static int GetFilenameAndSlot(uint32_t raw_slot, char filename[kStorageIdLengthMax], uint32_t* slot) { // Upper 16 bits should not be set if (raw_slot & 0xFFFF0000) { TLOGE("Error: Slot value %u invalid\n", raw_slot); return ERR_NOT_VALID; } // Mask type flag from raw slot to get index *slot = raw_slot & ~kTypeMask; if (*slot >= kRollbackSlotMax) { TLOGE("Error: Slot value %u larger than supported %u\n", *slot, kRollbackSlotMax); return ERR_NOT_VALID; } // Choose correct file for rollback index type strcpy(filename, kAvbRollbackFilename); int index_type = (raw_slot & kTypeMask) >> kTypeShift; char postfix[4]; snprintf(postfix, 4, ".%01X", index_type); strcat(filename, postfix); return NO_ERROR; } namespace avb { void AvbManager::ReadRollbackIndex(const RollbackIndexRequest& request, RollbackIndexResponse* response) { uint32_t slot; char filename[kStorageIdLengthMax]; if (GetFilenameAndSlot(request.get_slot(), filename, &slot) < 0) { response->set_error(AvbError::kInvalid); TLOGE("Error: Invalid slot value: %u\n", request.get_slot()); return; } int rc = storage_->open(filename); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open file %s: %d\n", filename, rc); return; } uint64_t size; rc = storage_->get_file_size(&size); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to get size of file %s: %d\n", filename, rc); return; } // If no valid rollback counter file is found, initialize to 0 uint64_t rollback_counter; if (size < kRollbackSlotMax * sizeof(uint64_t)) { TLOGD("No valid rollback index file found. Initializing to 0\n"); uint64_t write_buf[kRollbackSlotMax] = {0}; rc = storage_->write(0, write_buf, sizeof(write_buf)); rollback_counter = 0; } else { TLOGD("Rollback index file found\n"); rc = storage_->read(sizeof(rollback_counter) * slot, &rollback_counter, sizeof(rollback_counter)); } if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: reading storage object: %d\n", rc); return; } if (static_cast(rc) < sizeof(rollback_counter)) { response->set_error(AvbError::kInternal); TLOGE("Error: invalid object size: %d\n", rc); return; } response->set_value(rollback_counter); } void AvbManager::WriteRollbackIndex(const RollbackIndexRequest& request, RollbackIndexResponse* response) { uint32_t slot; char filename[kStorageIdLengthMax]; if (GetFilenameAndSlot(request.get_slot(), filename, &slot) < 0) { response->set_error(AvbError::kInvalid); TLOGE("Error: Invalid slot value: %u\n", request.get_slot()); return; } int rc = storage_->open(filename); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open file %s: %d\n", filename, rc); return; } uint64_t size; rc = storage_->get_file_size(&size); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to get size of file %s: %d\n", filename, rc); return; } // If no valid rollback counter file is found, initialize to 0 uint64_t request_value = request.get_value(); if (static_cast(size) < kRollbackSlotMax * sizeof(uint64_t)) { TLOGD("No valid rollback index file found. Initializing to 0\n"); uint64_t write_buf[kRollbackSlotMax] = {0}; write_buf[slot] = request_value; rc = storage_->write(0, write_buf, sizeof(write_buf)); } else { uint64_t rollback_counter; TLOGD("Found a rollback index file\n"); rc = storage_->read(sizeof(rollback_counter) * slot, &rollback_counter, sizeof(rollback_counter)); // Write value to specified slot in file if (request_value < rollback_counter) { response->set_error(AvbError::kInvalid); TLOGE("Error: Requested write [%lu] is less than existing counter value " "[%lu]\n", static_cast(request_value), static_cast(rollback_counter)); response->set_value(rollback_counter); return; } rc = storage_->write(sizeof(request_value) * slot, &request_value, sizeof(request_value)); } if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: accessing storage object [%d]\n", rc); return; } if (static_cast(rc) < sizeof(request_value)) { response->set_error(AvbError::kInternal); TLOGE("Error: invalid object size [%d]\n", rc); return; } response->set_value(request_value); } void AvbManager::GetVersion(const GetVersionRequest& request, GetVersionResponse* response) { response->set_version(kAvbVersion); } void AvbManager::ReadPermanentAttributes( const ReadPermanentAttributesRequest& request, ReadPermanentAttributesResponse* response) { int rc = storage_->open(kPermanentAttributesFilename); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open attributes file: %d\n", rc); return; } // Read permanent product attributes UniquePtr attributes(new uint8_t[kPermanentAttributesLengthMax]); rc = storage_->read(0, attributes.get(), kPermanentAttributesLengthMax); if (rc <= 0) { response->set_error(AvbError::kInternal); TLOGE("Error: %s attributes file [%d]\n", rc == 0 ? "missing" : "accessing", rc); return; } uint32_t attributes_size = static_cast(rc); response->set_attributes_buf(attributes.get(), attributes_size); } void AvbManager::WritePermanentAttributes( const WritePermanentAttributesRequest& request, WritePermanentAttributesResponse* response) { int rc = storage_->open(kPermanentAttributesFilename); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open attributes file: %d\n", rc); return; } uint64_t size; rc = storage_->get_file_size(&size); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to get size of attributes file: %d\n", rc); return; } if (size) { response->set_error(AvbError::kInvalid); TLOGE("Error: Permanent attributes already set!\n"); return; } // New file, write serialized permanent product attributes to storage uint32_t attributes_size = request.get_attributes_size(); uint8_t* attributes = request.get_attributes_buf(); rc = storage_->write(0, attributes, attributes_size); if (rc < 0 || static_cast(rc) < attributes_size) { response->set_error(AvbError::kInternal); TLOGE("Error: accessing storage object [%d]\n", rc); return; } } void AvbManager::ReadLockState(const ReadLockStateRequest& request, ReadLockStateResponse* response) { int rc = storage_->open(kLockStateFile); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open lock state file: %d\n", rc); return; } uint64_t size; rc = storage_->get_file_size(&size); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to get size of lock state file: %d\n", rc); return; } uint8_t lock_state = 1; if (size == 0) { // No lock state found, assume initial state is locked and write it rc = storage_->write(0, &lock_state, sizeof(lock_state)); } else { rc = storage_->read(0, &lock_state, sizeof(lock_state)); } if (rc < 0 || static_cast(rc) < sizeof(lock_state)) { response->set_error(AvbError::kInternal); TLOGE("Error: accessing storage object [%d]\n", rc); return; } response->set_lock_state(lock_state); } void AvbManager::WriteLockState(const WriteLockStateRequest& request, WriteLockStateResponse* response) { // Only 0 and 1 are valid lock states uint8_t request_lock_state = request.get_lock_state(); if (request_lock_state != 0 && request_lock_state != 1) { response->set_error(AvbError::kInvalid); TLOGE("Error: invalid lock state requested: %d\n", request_lock_state); return; } int rc = storage_->open(kLockStateFile); if (rc < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: failed to open lock state file: %d\n", rc); return; } // Try to read existing lock state uint8_t stored_lock_state = 0xFF; storage_->read(0, &stored_lock_state, sizeof(stored_lock_state)); // If no lock state file was found, or the device is changing lock state, // clear rollback index data if (stored_lock_state != request_lock_state) { if (DeleteRollbackIndexFiles() < 0) { response->set_error(AvbError::kInternal); TLOGE("Error: clearing rollback index data\n"); return; } } // Write new lock state rc = storage_->write(0, &request_lock_state, sizeof(request_lock_state)); if (rc < 0 || static_cast(rc) < sizeof(request_lock_state)) { response->set_error(AvbError::kInternal); TLOGE("Error: accessing storage object [%d]\n", rc); return; } } // Rollback counters are stored in files avb.rollback.{0..F}. When a device // changes lock state, data in rollback index files must be deleted. int AvbManager::DeleteRollbackIndexFiles() { char filename[kStorageIdLengthMax]; char postfix[4]; int rc = NO_ERROR; for (unsigned int i = 0x0; i <= 0xF; ++i) { strcpy(filename, kAvbRollbackFilename); snprintf(postfix, 4, ".%01X", i); strcat(filename, postfix); int storage_rc = storage_->delete_file(filename); if (storage_rc < 0 && storage_rc != ERR_NOT_FOUND) { rc = storage_rc; TLOGE("Error: deleting file %s: %d\n", filename, rc); continue; } } return rc; } }; // namespace avb