/* * Copyright 2019 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 "confirmationui" #ifdef WITH_HANDLE_PROT #include #endif #include #include #include #include #include #include #include #include #include #include "ipc.h" #include "trusty_operation.h" struct chan_ctx { void* shm_base; size_t shm_len; std::unique_ptr op; }; static inline bool is_inited(struct chan_ctx* ctx) { return ctx->shm_base; } static bool get_auth_token_key(teeui::AuthTokenKey& authKey) { long rc = keymaster_open(); bool result = false; if (rc < 0) { return false; } keymaster_session_t session = (keymaster_session_t)rc; uint8_t* key = nullptr; uint32_t local_length = 0; rc = keymaster_get_auth_token_key(session, &key, &local_length); keymaster_close(session); if (rc == NO_ERROR) { if (local_length == teeui::kAuthTokenKeySize) { /* Success - store the key */ memcpy(authKey.data(), key, teeui::kAuthTokenKeySize); result = true; } free(key); } return result; } struct __attribute__((__packed__)) confirmationui_req { struct confirmationui_hdr hdr; union { struct confirmationui_init_req init_args; struct confirmationui_msg_args msg_args; }; }; static int confirmationui_recv(handle_t chan, confirmationui_req* req, handle_t* h) { int rc; ipc_msg_info msg_info; uint32_t max_num_handles = h ? 1 : 0; struct iovec iov = { .iov_base = req, .iov_len = sizeof(*req), }; struct ipc_msg ipc_msg = { .num_iov = 1, .iov = &iov, .num_handles = max_num_handles, .handles = h, }; rc = get_msg(chan, &msg_info); if (rc != NO_ERROR) { TLOGE("Failed to get message (%d)\n", rc); return rc; } if (msg_info.len > sizeof(*req)) { TLOGE("Message is too long (%zd)\n", msg_info.len); rc = ERR_BAD_LEN; goto out; } if (msg_info.num_handles > max_num_handles) { TLOGE("Message has too many handles (%u)\n", msg_info.num_handles); rc = ERR_TOO_BIG; goto out; } rc = read_msg(chan, msg_info.id, 0, &ipc_msg); out: put_msg(chan, msg_info.id); return rc; } static int handle_init(handle_t chan, handle_t shm_handle, uint32_t shm_len, struct chan_ctx* ctx) { int rc; struct confirmationui_hdr hdr; if (is_inited(ctx)) { TLOGE("TA is already initialized.\n"); return ERR_BAD_STATE; } if (shm_len > CONFIRMATIONUI_MAX_MSG_SIZE) { TLOGE("Shared memory too long\n"); return ERR_BAD_LEN; } #ifdef WITH_HANDLE_PROT rc = handle_prot_secure(shm_handle, false); if (rc < 0) { TLOGE("Shared memory should be non-secure, rc=%d\n", rc); return rc; } rc = handle_prot_prot_id(shm_handle, 0); if (rc < 0) { TLOGE("Shared memory prot Id invalid, rc=%d.\n", rc); return rc; } #endif void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, 0, shm_handle, 0); if (shm_base == MAP_FAILED) { TLOGE("Failed to mmap() handle\n"); return ERR_BAD_HANDLE; } hdr.cmd = CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT; rc = tipc_send1(chan, &hdr, sizeof(hdr)); if (rc != (int)sizeof(hdr)) { TLOGE("Failed to send response (%d)\n", rc); if (rc >= 0) { rc = ERR_BAD_LEN; } goto err; } ctx->shm_base = shm_base; ctx->shm_len = shm_len; return NO_ERROR; err: int rc1 = munmap(shm_base, shm_len); if (rc1 != NO_ERROR) { TLOGW("munmap() failed: %d\n", rc1); } return rc; } static int handle_msg(handle_t chan, uint32_t req_len, struct chan_ctx* ctx) { int rc; uint8_t msg[CONFIRMATIONUI_MAX_MSG_SIZE]; uint32_t resp_len = ctx->shm_len; struct confirmationui_hdr hdr; struct confirmationui_msg_args args; if (!is_inited(ctx)) { TLOGE("TA is not initialized.\n"); return ERR_BAD_STATE; } if (req_len > ctx->shm_len) { TLOGE("Message too long (%u)\n", req_len); return ERR_BAD_LEN; } assert(req_len <= sizeof(msg)); memcpy(msg, ctx->shm_base, req_len); ctx->op->handleMsg(msg, req_len, ctx->shm_base, &resp_len); hdr.cmd = CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT; args.msg_len = resp_len; rc = tipc_send2(chan, &hdr, sizeof(hdr), &args, sizeof(args)); if (rc != (int)(sizeof(hdr) + sizeof(args))) { TLOGE("Failed to send response (%d)\n", rc); if (rc >= 0) { rc = ERR_BAD_LEN; } return rc; } return NO_ERROR; } static int on_connect(const struct tipc_port* port, handle_t chan, const struct uuid* peer, void** ctx_p) { auto op = std::make_unique(); if (!op) { TLOGE("Failed to allocate TrustyOperation\n"); return ERR_NO_MEMORY; } struct chan_ctx* ctx = (struct chan_ctx*)calloc(1, sizeof(*ctx)); if (!ctx) { TLOGE("Failed to allocate channel context\n"); return ERR_NO_MEMORY; } #if defined(PLATFORM_GENERIC_ARM64) /* Use the test key for emulator. */ constexpr const auto kTestKey = teeui::AuthTokenKey::fill( static_cast(teeui::TestKeyBits::BYTE)); op->setHmacKey(kTestKey); #else teeui::AuthTokenKey authKey; if (get_auth_token_key(authKey) == true) { TLOGD("%s, get auth token key successfully\n", __func__); } else { TLOGE("%s, get auth token key failed\n", __func__); /* Abort operation and free all resources. */ op->abort(); free(ctx); return ERR_GENERIC; } op->setHmacKey(authKey); #endif ctx->op = std::move(op); *ctx_p = ctx; return NO_ERROR; } static void on_channel_cleanup(void* _ctx) { struct chan_ctx* ctx = (struct chan_ctx*)_ctx; /* Abort operation and free all resources. */ int rc = munmap(ctx->shm_base, ctx->shm_len); if (rc != NO_ERROR) { TLOGW("munmap() failed: %d\n", rc); } ctx->op->abort(); ctx->op.reset(); free(ctx); } static int on_message(const struct tipc_port* port, handle_t chan, void* _ctx) { int rc; struct confirmationui_req req; handle_t shm_handle = INVALID_IPC_HANDLE; struct chan_ctx* ctx = (struct chan_ctx*)_ctx; assert(ctx); rc = confirmationui_recv(chan, &req, &shm_handle); if (rc < 0) { TLOGE("Failed to receive confirmationui request (%d)\n", rc); return rc; } if (rc != (int)sizeof(req)) { TLOGE("Receive request of unexpected size(%d)\n", rc); rc = ERR_BAD_LEN; goto out; } switch (req.hdr.cmd) { case CONFIRMATIONUI_CMD_INIT: rc = handle_init(chan, shm_handle, req.init_args.shm_len, ctx); goto out; case CONFIRMATIONUI_CMD_MSG: rc = handle_msg(chan, req.msg_args.msg_len, ctx); goto out; default: TLOGE("cmd 0x%x: unknown command\n", req.hdr.cmd); rc = ERR_CMD_UNKNOWN; goto out; } out: close(shm_handle); return rc; } static struct tipc_port_acl confirmationui_port_acl = { .flags = IPC_PORT_ALLOW_NS_CONNECT, }; static struct tipc_port confirmationui_port = { .name = CONFIRMATIONUI_PORT, .msg_max_size = sizeof(confirmationui_req), .msg_queue_len = 1, .acl = &confirmationui_port_acl, }; static struct tipc_srv_ops confirmationui_ops = { .on_connect = on_connect, .on_message = on_message, .on_channel_cleanup = on_channel_cleanup, }; int main(void) { int rc; struct tipc_hset* hset; TLOGD("Initializing ConfirmationUI app\n"); hset = tipc_hset_create(); if (IS_ERR(hset)) { TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset)); return PTR_ERR(hset); } rc = tipc_add_service(hset, &confirmationui_port, 1, 1, &confirmationui_ops); if (rc != NO_ERROR) { return rc; } return tipc_run_event_loop(hset); }