/* * 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 #include #include #include "avb_ipc.h" #include "avb_manager.h" #include "secure_storage.h" const char kAvbServiceName[] = "com.android.trusty.avb"; const int kAvbServiceNumBufs = 1; const int kAvbServiceBufSize = 2048; const uint32_t kAvbServiceFlags = IPC_PORT_ALLOW_NS_CONNECT; static bool boot_state_locked = false; avb::AvbManager* g_avb_manager; namespace avb { // Deserializes request in |in_buf|, calls |operation| to execute request, // and serializes response to |out_buf|. Returns a Trusty error code if there // is a problem during serializing the response or deserializing the request. template static int ExecuteCommand(void (AvbManager::*operation)(const Request&, Response*), const uint8_t* in_buf, uint32_t in_size, UniquePtr* out_buf, uint32_t* out_size, AvbError* error) { Request req; int rc = req.Deserialize(in_buf, in_buf + in_size); if (rc < 0) { TLOGE("Error deserializing request: %d\n", rc); return rc; } Response rsp; (g_avb_manager->*operation)(req, &rsp); *error = rsp.get_error(); *out_size = rsp.GetSerializedSize(); if (*out_size > kAvbServiceBufSize) { TLOGE("Response size too large: %d\n", *out_size); *out_size = 0; return ERR_TOO_BIG; } if (*out_size == 0) { return NO_ERROR; } out_buf->reset(new uint8_t[*out_size]); if (out_buf->get() == nullptr) { *out_size = 0; return ERR_NO_MEMORY; } if (rsp.Serialize(out_buf->get(), out_buf->get() + *out_size) != *out_size) { TLOGE("Error serializing response message\n"); return ERR_NOT_VALID; } return NO_ERROR; } // Dispatches command |cmd|. Fulfills request in |in_buf|, and writes response // to |out_buf|. Returns a Trusty error code if there is a problem serializing // the response or deserializing the request. Avb specific errors are put in // |error|. static int ProcessRequest(uint32_t cmd, const uint8_t* in_buf, uint32_t in_size, UniquePtr* out_buf, uint32_t* out_size, AvbError* error) { if (boot_state_locked) { switch (cmd) { case WRITE_ROLLBACK_INDEX: case WRITE_PERMANENT_ATTRIBUTES: case WRITE_LOCK_STATE: *out_size = 0; *error = AvbError::kInvalid; return NO_ERROR; } } switch (cmd) { case READ_ROLLBACK_INDEX: return ExecuteCommand(&AvbManager::ReadRollbackIndex, in_buf, in_size, out_buf, out_size, error); case WRITE_ROLLBACK_INDEX: return ExecuteCommand(&AvbManager::WriteRollbackIndex, in_buf, in_size, out_buf, out_size, error); case AVB_GET_VERSION: return ExecuteCommand(&AvbManager::GetVersion, in_buf, in_size, out_buf, out_size, error); case READ_PERMANENT_ATTRIBUTES: return ExecuteCommand(&AvbManager::ReadPermanentAttributes, in_buf, in_size, out_buf, out_size, error); case WRITE_PERMANENT_ATTRIBUTES: return ExecuteCommand(&AvbManager::WritePermanentAttributes, in_buf, in_size, out_buf, out_size, error); case READ_LOCK_STATE: return ExecuteCommand(&AvbManager::ReadLockState, in_buf, in_size, out_buf, out_size, error); case WRITE_LOCK_STATE: return ExecuteCommand(&AvbManager::WriteLockState, in_buf, in_size, out_buf, out_size, error); case LOCK_BOOT_STATE: *out_size = 0; *error = AvbError::kNone; boot_state_locked = true; return NO_ERROR; default: return ERR_NOT_VALID; } } // Reads the message described by |msg_info| from channel and processes it. // Writes the response message back to |channel|. Returns a Trusty error code if // there is any I/O problem. static int ProcessOneMessage(handle_t channel, const ipc_msg_info_t& msg_info) { if (msg_info.len > kAvbServiceBufSize) { TLOGE("Message too large on channel %x: %zu", channel, msg_info.len); return ERR_TOO_BIG; } UniquePtr msg_buf(new uint8_t[msg_info.len]); struct iovec request_iov = {msg_buf.get(), msg_info.len}; ipc_msg_t request_msg = { 1, // number of iovecs &request_iov, // iovecs pointer 0, // number of handles nullptr // handles pointer }; int rc = read_msg(channel, msg_info.id, 0, &request_msg); if (rc < 0) { TLOGE("Failed to read msg for channel %x: %d\n", channel, rc); return rc; } if (((size_t)rc) < sizeof(avb_message)) { TLOGE("Invalid message of size %zu for channel %x\n", (size_t)rc, channel); return ERR_NOT_VALID; } // Parse and handle request avb_message* avb_request_header = reinterpret_cast(msg_buf.get()); UniquePtr out_buf; uint32_t out_size = 0; AvbError error; rc = ProcessRequest(avb_request_header->cmd, avb_request_header->payload, msg_info.len - sizeof(avb_message), &out_buf, &out_size, &error); if (rc < 0) { TLOGE("Unable to handle request: %d", rc); return rc; } // Send response message back to caller avb_message avb_response_header = { .cmd = avb_request_header->cmd | AVB_RESP_BIT, .result = error, .payload = {}, }; struct iovec response_iov[2] = { {&avb_response_header, sizeof(avb_response_header)}, {out_buf.get(), out_size}, }; ipc_msg_t response_msg = { 2, // number of iovecs response_iov, // iovecs pointer 0, // number of handles nullptr // handles pointer }; rc = send_msg(channel, &response_msg); if (rc < 0) { TLOGE("Failed to send_msg on channel %x: %d\n", channel, rc); return rc; } return NO_ERROR; } // Receives all pending messages from |channel| and processes them, sending // responses as appropriate. Returns a Trusty error code if there is a // problem with message processing or I/O. static int ProcessMessages(handle_t channel) { while (true) { ipc_msg_info_t msg_info; int rc = get_msg(channel, &msg_info); if (rc == ERR_NO_MSG) { break; } if (rc != NO_ERROR) { TLOGE("Failed to get_msg on channel %x: %d\n", channel, rc); return rc; } rc = ProcessOneMessage(channel, msg_info); if (rc != NO_ERROR) { put_msg(channel, msg_info.id); return rc; } rc = put_msg(channel, msg_info.id); if (rc != NO_ERROR) { TLOGE("Failed to put_msg on channel %x: %d\n", rc, channel); return rc; } } return NO_ERROR; } // Handles an |event| on a channel. If it is an incoming message, it is // processed. Otherwise, the event indicates a channel hangup or error, so // the channel is closed. static void HandleChannelEvent(const uevent_t& event) { if ((event.event & IPC_HANDLE_POLL_ERROR) || (event.event & IPC_HANDLE_POLL_READY)) { // close it as it is in an error state TLOGE("Error event on channel %x: 0x%x\n", event.event, event.handle); close(event.handle); } if (event.event & IPC_HANDLE_POLL_HUP) { // closed by peer close(event.handle); return; } if (event.event & IPC_HANDLE_POLL_MSG) { if (ProcessMessages(event.handle) != NO_ERROR) { close(event.handle); } } } // Handles an |event| on a port. If it is an incoming connection event, // the connection is accepted. Any other event is unexpected. static void HandlePortEvent(const uevent_t& event) { if (event.event & IPC_HANDLE_POLL_READY) { // incoming connection: accept it uuid_t peer_uuid; int rc = accept(event.handle, &peer_uuid); if (rc < 0) { TLOGE("Failed to accept on port %d: %d\n", event.handle, rc); } } else { TLOGE("Unexpected event on port %d: 0x%x\n", event.handle, event.event); } } } // namespace avb int main(void) { avb::AvbManager avb_manager(new avb::SecureStorage); g_avb_manager = &avb_manager; TLOGD("Initializing AVB App\n"); int rc = port_create(kAvbServiceName, kAvbServiceNumBufs, kAvbServiceBufSize, kAvbServiceFlags); if (rc < 0) { TLOGE("Failed to initialize AVB app: %d", rc); return rc; } handle_t port = static_cast(rc); // enter main event loop while (true) { uevent_t event; event.handle = INVALID_IPC_HANDLE; event.event = 0; event.cookie = nullptr; rc = wait_any(&event, INFINITE_TIME); if (rc == NO_ERROR) { // got an event if (event.handle == port) { avb::HandlePortEvent(event); } else { avb::HandleChannelEvent(event); } } else { TLOGE("wait_any failed: %d\n", rc); } } g_avb_manager = nullptr; return 0; }