blob: 92730c32f44b45ace510a513dfda20393eef5516 [file] [log] [blame]
/*
* Copyright (C) 2020 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.
*/
#define TLOG_TAG "apploader"
#include <assert.h>
#include <endian.h>
#include <interface/apploader/apploader.h>
#include <interface/apploader/apploader_secure.h>
#include <inttypes.h>
#include <lib/app_manifest/app_manifest.h>
#include <lib/system_state/system_state.h>
#include <lib/tipc/tipc.h>
#include <lib/tipc/tipc_srv.h>
#include <lk/err_ptr.h>
#include <lk/macros.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <trusty_log.h>
#include "app_version.h"
#include "apploader_package.h"
struct apploader_req {
struct apploader_header hdr;
union {
struct apploader_load_app_req load_app_req;
};
} __PACKED;
/*
* Common structure covering all possible apploader messages, only used to
* determine the maximum message size
*/
union apploader_longest_msg {
struct apploader_req req;
struct apploader_resp resp;
} __PACKED;
static struct tipc_port_acl apploader_port_acl = {
#ifdef APPLOADER_ALLOW_NS_CONNECT
.flags = IPC_PORT_ALLOW_TA_CONNECT | IPC_PORT_ALLOW_NS_CONNECT,
#else
.flags = IPC_PORT_ALLOW_TA_CONNECT,
#endif
.uuid_num = 0,
.uuids = NULL,
.extra_data = NULL,
};
static struct tipc_port apploader_port = {
.name = APPLOADER_PORT,
.msg_max_size = sizeof(union apploader_longest_msg),
.msg_queue_len = 1,
.acl = &apploader_port_acl,
.priv = NULL,
};
static int apploader_translate_error(int rc) {
if (rc > 0) {
return APPLOADER_ERR_INTERNAL;
}
switch (rc) {
case ERR_NO_MEMORY:
return APPLOADER_ERR_NO_MEMORY;
case ERR_ALREADY_EXISTS:
return APPLOADER_ERR_ALREADY_EXISTS;
default:
TLOGW("Unrecognized error (%d)\n", rc);
return APPLOADER_ERR_INTERNAL;
}
}
static int apploader_send_response(handle_t chan,
uint32_t cmd,
uint32_t error) {
struct apploader_resp resp = {
.hdr =
{
.cmd = cmd | APPLOADER_RESP_BIT,
},
.error = error,
};
int rc = tipc_send1(chan, &resp, sizeof(resp));
if (rc < 0) {
return rc;
}
if ((size_t)rc != sizeof(resp)) {
TLOGE("Failed to send message (%d). Expected to send %zu bytes.\n", rc,
sizeof(resp));
return ERR_BAD_LEN;
}
return NO_ERROR;
}
static int apploader_read(handle_t chan,
size_t min_sz,
void* buf,
size_t buf_sz,
handle_t* handles,
uint32_t* num_handles) {
int rc;
ipc_msg_info_t msg_inf;
rc = get_msg(chan, &msg_inf);
if (rc != NO_ERROR) {
TLOGE("Failed to get message (%d)\n", rc);
return rc;
}
if (msg_inf.len < min_sz || msg_inf.len > buf_sz) {
TLOGE("Message is too short or too long (%zd)\n", msg_inf.len);
rc = ERR_BAD_LEN;
goto err;
}
uint32_t max_num_handles = num_handles ? *num_handles : 0;
if (msg_inf.num_handles > max_num_handles) {
TLOGE("Message has too many handles (%" PRIu32 ")\n",
msg_inf.num_handles);
rc = ERR_TOO_BIG;
goto err;
}
struct iovec iov = {
.iov_base = buf,
.iov_len = buf_sz,
};
ipc_msg_t ipc_msg = {
.iov = &iov,
.num_iov = 1,
.handles = handles,
.num_handles = msg_inf.num_handles,
};
rc = read_msg(chan, msg_inf.id, 0, &ipc_msg);
assert(rc < 0 || (size_t)rc == msg_inf.len);
if (rc >= 0 && num_handles) {
*num_handles = msg_inf.num_handles;
}
err:
put_msg(chan, msg_inf.id);
return rc;
}
static uint32_t apploader_send_secure_get_memory_command(
handle_t secure_chan,
size_t aligned_size,
handle_t* secure_mem_handle) {
assert(secure_mem_handle);
struct apploader_secure_header hdr = {
.cmd = APPLOADER_SECURE_CMD_GET_MEMORY,
};
struct apploader_secure_get_memory_req get_memory_req = {
.package_size = aligned_size,
};
int rc = tipc_send2(secure_chan, &hdr, sizeof(hdr), &get_memory_req,
sizeof(get_memory_req));
if ((size_t)rc != sizeof(hdr) + sizeof(get_memory_req)) {
TLOGE("Failed to send get_memory message (%d)\n", rc);
return apploader_translate_error(rc);
}
uevent_t event = UEVENT_INITIAL_VALUE(event);
rc = wait(secure_chan, &event, INFINITE_TIME);
if (rc != NO_ERROR || !(event.event & IPC_HANDLE_POLL_MSG)) {
TLOGE("Failed to wait for response (%d)\n", rc);
return apploader_translate_error(rc);
}
uint32_t num_handles = 1;
struct apploader_secure_resp resp;
rc = apploader_read(secure_chan, sizeof(resp), &resp, sizeof(resp),
secure_mem_handle, &num_handles);
if (rc < 0) {
TLOGE("Failed to read response (%d)\n", rc);
return apploader_translate_error(rc);
}
if (resp.hdr.cmd !=
(APPLOADER_SECURE_CMD_GET_MEMORY | APPLOADER_SECURE_RESP_BIT)) {
TLOGE("Invalid command in response (%u)\n", resp.hdr.cmd);
return APPLOADER_ERR_INTERNAL;
}
if (resp.error != APPLOADER_NO_ERROR) {
TLOGE("Received error from service (%" PRIu32 ")\n", resp.error);
return resp.error;
}
if (num_handles != 1) {
TLOGE("Expected 1 handle, got %" PRIu32 "\n", num_handles);
return APPLOADER_ERR_INTERNAL;
}
return APPLOADER_NO_ERROR;
}
static uint32_t apploader_send_secure_load_command(
handle_t secure_chan,
ptrdiff_t elf_offset,
ptrdiff_t manifest_offset,
struct apploader_package_metadata* pkg_meta) {
struct apploader_secure_header hdr = {
.cmd = APPLOADER_SECURE_CMD_LOAD_APPLICATION,
};
struct apploader_secure_load_app_req req = {
.manifest_start = manifest_offset,
.manifest_end = manifest_offset + pkg_meta->manifest_size,
.img_start = elf_offset,
.img_end = elf_offset + pkg_meta->elf_size,
};
int rc = tipc_send2(secure_chan, &hdr, sizeof(hdr), &req, sizeof(req));
if (rc != sizeof(hdr) + sizeof(req)) {
TLOGE("Failed to send message (%d). Expected to send %zu bytes.\n", rc,
sizeof(hdr) + sizeof(req));
return apploader_translate_error(rc);
}
uevent_t event = UEVENT_INITIAL_VALUE(event);
rc = wait(secure_chan, &event, INFINITE_TIME);
if (rc != NO_ERROR || !(event.event & IPC_HANDLE_POLL_MSG)) {
TLOGE("Failed to wait for response (%d)\n", rc);
return apploader_translate_error(rc);
}
struct apploader_secure_resp resp;
rc = tipc_recv1(secure_chan, sizeof(resp), &resp, sizeof(resp));
if ((size_t)rc != sizeof(resp)) {
TLOGE("Failed to read response for load application command\n");
return apploader_translate_error(rc);
}
if (resp.hdr.cmd !=
(APPLOADER_SECURE_CMD_LOAD_APPLICATION | APPLOADER_SECURE_RESP_BIT)) {
TLOGE("Invalid command in response (%u)\n", resp.hdr.cmd);
return APPLOADER_ERR_INTERNAL;
}
if (resp.error != APPLOADER_NO_ERROR) {
TLOGE("Received error from service (%" PRIu32 ")\n", resp.error);
return resp.error;
}
return APPLOADER_NO_ERROR;
}
static uint32_t apploader_copy_package(handle_t req_handle,
handle_t secure_chan,
uint64_t aligned_size,
uint8_t** out_package) {
uint32_t resp_error;
handle_t secure_mem_handle;
uint32_t get_memory_error = apploader_send_secure_get_memory_command(
secure_chan, aligned_size, &secure_mem_handle);
if (get_memory_error != APPLOADER_NO_ERROR) {
TLOGE("Failed to get memory from service (%" PRIu32 ")\n",
get_memory_error);
resp_error = get_memory_error;
goto err_send_get_memory;
}
if (secure_mem_handle == INVALID_IPC_HANDLE) {
TLOGE("Received invalid handle from service\n");
resp_error = APPLOADER_ERR_INTERNAL;
goto err_invalid_secure_mem_handle;
}
void* req_package = mmap(NULL, aligned_size, PROT_READ, 0, req_handle, 0);
if (req_package == MAP_FAILED) {
TLOGE("Failed to map the request handle\n");
resp_error = APPLOADER_ERR_NO_MEMORY;
goto err_req_mmap;
}
void* resp_package = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, 0,
secure_mem_handle, 0);
if (resp_package == MAP_FAILED) {
TLOGE("Failed to map the handle from service\n");
resp_error = APPLOADER_ERR_NO_MEMORY;
goto err_resp_mmap;
}
assert(out_package);
memcpy(resp_package, req_package, aligned_size);
*out_package = resp_package;
resp_error = APPLOADER_NO_ERROR;
err_resp_mmap:
munmap(req_package, aligned_size);
err_req_mmap:
close(secure_mem_handle);
err_invalid_secure_mem_handle:
err_send_get_memory:
return resp_error;
}
static bool apploader_relocate_package(
uint8_t* package,
struct apploader_package_metadata* pkg_meta) {
if (pkg_meta->elf_start > pkg_meta->manifest_start) {
/*
* For now, we only support input files where the ELF precedes
* the manifest. The current file format follows this rule.
*/
return false;
}
uint64_t unaligned_elf_size = pkg_meta->elf_size;
uint64_t page_size = getauxval(AT_PAGESZ);
/* ELF comes first, move it to offset 0 */
memmove(package, pkg_meta->elf_start, unaligned_elf_size);
pkg_meta->elf_start = package;
pkg_meta->elf_size = round_up(unaligned_elf_size, page_size);
if (pkg_meta->elf_size > unaligned_elf_size) {
/*
* There is a gap between ELF and manifest, zero it because it will
* probably be used for .bss
*/
memset(package + unaligned_elf_size, 0,
pkg_meta->elf_size - unaligned_elf_size);
}
/*
* Then move the manifest just after; the manifest starts
* on the page immediately after the ELF file (elf_size is page-aligned
* by the round_up call above), so the two never share a page
*/
uint8_t* new_manifest_start = package + pkg_meta->elf_size;
memmove(new_manifest_start, pkg_meta->manifest_start,
pkg_meta->manifest_size);
pkg_meta->manifest_start = new_manifest_start;
return true;
}
static int apploader_handle_cmd_load_app(handle_t chan,
struct apploader_load_app_req* req,
handle_t req_handle) {
uint32_t resp_error;
if (req_handle == INVALID_IPC_HANDLE) {
TLOGE("Received invalid request handle\n");
resp_error = APPLOADER_ERR_INVALID_CMD;
goto err_invalid_req_handle;
}
uint64_t page_size = getauxval(AT_PAGESZ);
uint64_t aligned_size = round_up(req->package_size, page_size);
TLOGD("Loading %" PRIu64 " bytes package, %" PRIu64 " aligned\n",
req->package_size, aligned_size);
handle_t secure_chan;
int rc = tipc_connect(&secure_chan, APPLOADER_SECURE_PORT);
if (rc < 0) {
TLOGE("Failed to connect to service (%d)\n", rc);
resp_error = apploader_translate_error(rc);
goto err_connect_secure;
}
uint32_t copy_error;
uint8_t* package;
copy_error = apploader_copy_package(req_handle, secure_chan, aligned_size,
&package);
if (copy_error != APPLOADER_NO_ERROR) {
TLOGE("Failed to copy package from client\n");
resp_error = copy_error;
goto err_copy_package;
}
struct apploader_package_metadata pkg_meta = {0};
if (!apploader_parse_package_metadata(package, req->package_size,
&pkg_meta)) {
TLOGE("Failed to parse application package\n");
resp_error = APPLOADER_ERR_VERIFICATION_FAILED;
goto err_invalid_package;
}
if (!pkg_meta.manifest_start || !pkg_meta.manifest_size) {
TLOGE("Could not find manifest in application package\n");
resp_error = APPLOADER_ERR_VERIFICATION_FAILED;
goto err_manifest_not_found;
}
if (!pkg_meta.elf_start || !pkg_meta.elf_size) {
TLOGE("Could not find ELF image in application package\n");
resp_error = APPLOADER_ERR_VERIFICATION_FAILED;
goto err_elf_not_found;
}
if (!system_state_app_loading_skip_version_check() &&
!apploader_check_app_version(&pkg_meta)) {
TLOGE("Failed application version check\n");
resp_error = APPLOADER_ERR_INVALID_VERSION;
goto err_version_check;
}
if (!apploader_relocate_package(package, &pkg_meta)) {
TLOGE("Failed to relocate package contents in memory\n");
resp_error = APPLOADER_ERR_VERIFICATION_FAILED;
goto err_relocate_package;
}
ptrdiff_t elf_offset = pkg_meta.elf_start - package;
ptrdiff_t manifest_offset = pkg_meta.manifest_start - package;
munmap(package, aligned_size);
package = NULL;
/* Validate the relocated offsets */
assert(elf_offset >= 0);
assert(elf_offset + pkg_meta.elf_size <= aligned_size);
assert(manifest_offset >= 0);
assert(manifest_offset + pkg_meta.manifest_size <= aligned_size);
/* Finalize the loading by sending a LOAD_APPLICATION request */
resp_error = apploader_send_secure_load_command(secure_chan, elf_offset,
manifest_offset, &pkg_meta);
if (resp_error != APPLOADER_NO_ERROR) {
TLOGE("Failed to load application (%" PRIu32 ")\n", resp_error);
}
err_relocate_package:
err_version_check:
err_elf_not_found:
err_manifest_not_found:
err_invalid_package:
if (package) {
munmap(package, aligned_size);
}
err_copy_package:
close(secure_chan);
err_connect_secure:
err_invalid_req_handle:
return apploader_send_response(chan, APPLOADER_CMD_LOAD_APPLICATION,
resp_error);
}
static int apploader_on_message(const struct tipc_port* port,
handle_t chan,
void* ctx) {
assert(port == &apploader_port);
assert(ctx == NULL);
int rc;
handle_t handle = INVALID_IPC_HANDLE;
uint32_t num_handles = 1;
struct apploader_req req;
rc = apploader_read(chan, sizeof(req.hdr), &req, sizeof(req), &handle,
&num_handles);
if (rc < 0) {
TLOGE("Failed to read request (%d)\n", rc);
return rc;
}
TLOGD("Command: 0x%x\n", req.hdr.cmd);
size_t cmd_len;
switch (req.hdr.cmd) {
case APPLOADER_CMD_LOAD_APPLICATION:
/* Check the message length */
cmd_len = sizeof(req.hdr) + sizeof(req.load_app_req);
if (rc != (int)cmd_len) {
TLOGE("Expected to read %zu bytes, got %d.\n", cmd_len, rc);
rc = apploader_send_response(chan, req.hdr.cmd,
APPLOADER_ERR_INVALID_CMD);
break;
}
if (num_handles != 1) {
TLOGE("Expected 1 handle, got %" PRIu32 "\n", num_handles);
rc = apploader_send_response(chan, req.hdr.cmd,
APPLOADER_ERR_INVALID_CMD);
break;
}
rc = apploader_handle_cmd_load_app(chan, &req.load_app_req, handle);
break;
default:
TLOGE("Received unknown apploader command: %" PRIu32 "\n", req.hdr.cmd);
rc = apploader_send_response(chan, req.hdr.cmd,
APPLOADER_ERR_UNKNOWN_CMD);
break;
}
if (rc < 0) {
TLOGE("Failed to run command (%d)\n", rc);
}
if (handle != INVALID_IPC_HANDLE) {
close(handle);
}
return rc;
}
static struct tipc_srv_ops apploader_ops = {
.on_message = apploader_on_message,
};
int main(void) {
struct tipc_hset* hset = tipc_hset_create();
if (IS_ERR(hset)) {
return PTR_ERR(hset);
}
int rc = tipc_add_service(hset, &apploader_port, 1, 1, &apploader_ops);
if (rc < 0) {
return rc;
}
rc = tipc_run_event_loop(hset);
printf("app loader going down: (%d)\n", rc);
return rc;
}