blob: 414769d1c950865dcc1c907c8ce9496b3b150da9 [file] [log] [blame]
/*
* 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 <string.h>
#include <uapi/err.h>
#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<size_t>(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_t>(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<long unsigned>(request_value),
static_cast<long unsigned>(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<size_t>(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<uint8_t[]> 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<uint32_t>(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<size_t>(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<size_t>(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<size_t>(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