| /* |
| * Copyright (C) 2015 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. |
| */ |
| |
| // TODO: add guard in header |
| extern "C" { |
| #include <stdlib.h> |
| } |
| |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include <trusty_std.h> |
| #include <trusty_ipc.h> |
| |
| #include <err.h> |
| |
| #include "trusty_gatekeeper.h" |
| #include "gatekeeper_ipc.h" |
| |
| using namespace gatekeeper; |
| TrustyGateKeeper *device; |
| |
| class SessionManager { |
| public: |
| SessionManager(long* err) { |
| *err = device->OpenSession(); |
| } |
| ~SessionManager() { |
| device->CloseSession(); |
| } |
| }; |
| |
| class MessageDeleter { |
| public: |
| explicit MessageDeleter(handle_t chan, int id) { |
| chan_ = chan; |
| id_ = id; |
| } |
| |
| ~MessageDeleter() { |
| put_msg(chan_, id_); |
| } |
| |
| private: |
| handle_t chan_; |
| int id_; |
| }; |
| |
| |
| static gatekeeper_error_t tipc_err_to_gatekeeper_err(long tipc_err) { |
| switch (tipc_err) { |
| case NO_ERROR: |
| return ERROR_NONE; |
| case ERR_BAD_LEN: |
| case ERR_NOT_VALID: |
| case ERR_NOT_IMPLEMENTED: |
| case ERR_NOT_SUPPORTED: |
| return ERROR_INVALID; |
| default: |
| return ERROR_UNKNOWN; |
| } |
| } |
| |
| template <typename Request, typename Response> |
| static gatekeeper_error_t exec_cmd(void (GateKeeper::*operation)(const Request&, Response*), |
| uint8_t *in_buf, uint32_t in_size, |
| UniquePtr<uint8_t[]> *out_buf, uint32_t *out_size) { |
| long rc; |
| SessionManager sm(&rc); |
| if (rc != NO_ERROR) |
| return tipc_err_to_gatekeeper_err(rc); |
| |
| Request req; |
| gatekeeper_error_t err = req.Deserialize(in_buf, in_buf + in_size); |
| if (err != ERROR_NONE) { |
| TLOGE("error (%d) deserializing request\n", err); |
| return ERROR_INVALID; |
| } |
| |
| Response rsp; |
| (device->*operation)(req, &rsp); |
| |
| *out_size = rsp.GetSerializedSize(); |
| if (*out_size > GATEKEEPER_MAX_BUFFER_LENGTH) { |
| *out_size = 0; |
| TLOGE("response size too large (%d)\n", *out_size); |
| return ERROR_UNKNOWN; |
| } |
| |
| out_buf->reset(new uint8_t[*out_size]); |
| if (out_buf->get() == NULL) { |
| *out_size = 0; |
| return ERROR_UNKNOWN; |
| } |
| |
| if(rsp.Serialize(out_buf->get(), out_buf->get() + *out_size) != *out_size) { |
| TLOGE("error serializing response message\n"); |
| return ERROR_UNKNOWN; |
| } |
| |
| return ERROR_NONE; |
| } |
| |
| static gatekeeper_error_t handle_request(uint32_t cmd, uint8_t *in_buf, uint32_t in_buf_size, |
| UniquePtr<uint8_t[]> *out_buf, uint32_t *out_buf_size) { |
| switch (cmd) { |
| case GK_ENROLL: |
| return exec_cmd(&GateKeeper::Enroll, in_buf, in_buf_size, |
| out_buf, out_buf_size); |
| case GK_VERIFY: |
| return exec_cmd(&GateKeeper::Verify, in_buf, in_buf_size, |
| out_buf, out_buf_size); |
| default: |
| return ERROR_INVALID; |
| } |
| } |
| |
| |
| static gatekeeper_error_t send_response(handle_t chan, |
| uint32_t cmd, uint8_t *out_buf, uint32_t out_buf_size) { |
| struct gatekeeper_message gk_msg = { cmd | GK_RESP_BIT, {}}; |
| iovec_t iov[2] = { |
| { &gk_msg, sizeof(gk_msg) }, |
| { out_buf, out_buf_size }, |
| }; |
| ipc_msg_t msg = { 2, iov, 0, NULL }; |
| |
| /* send message back to the caller */ |
| long rc = send_msg(chan, &msg); |
| |
| // fatal error |
| if (rc < 0) { |
| TLOGE("failed (%ld) to send_msg for chan (%d)\n", rc, chan); |
| return tipc_err_to_gatekeeper_err(rc); |
| } |
| |
| return ERROR_NONE; |
| } |
| |
| static gatekeeper_error_t send_error_response(handle_t chan, uint32_t cmd, gatekeeper_error_t err) { |
| GateKeeperMessage msg(err); |
| size_t serialized_size = msg.GetSerializedSize(); |
| uint8_t *out_buf = new uint8_t[serialized_size]; |
| if (out_buf == NULL) { |
| return ERROR_UNKNOWN; |
| } |
| |
| msg.Serialize(out_buf, out_buf + serialized_size); |
| gatekeeper_error_t rc = send_response(chan, cmd, out_buf, serialized_size); |
| |
| delete[] out_buf; |
| return rc; |
| } |
| |
| |
| static long handle_msg(handle_t chan) { |
| /* get message info */ |
| ipc_msg_info_t msg_inf; |
| |
| long rc = get_msg(chan, &msg_inf); |
| if (rc == ERR_NO_MSG) |
| return ERROR_NONE; /* no new messages */ |
| |
| // fatal error |
| if (rc != NO_ERROR) { |
| TLOGE("failed (%ld) to get_msg for chan (%d), closing connection\n", |
| rc, chan); |
| return rc; |
| } |
| |
| MessageDeleter md(chan, msg_inf.id); |
| |
| UniquePtr<uint8_t[]> msg_buf(new uint8_t[msg_inf.len]); |
| |
| /* read msg content */ |
| iovec_t iov = { msg_buf.get(), msg_inf.len }; |
| ipc_msg_t msg = { 1, &iov, 0, NULL} ; |
| |
| rc = read_msg(chan, msg_inf.id, 0, &msg); |
| |
| if (rc < 0) { |
| TLOGE("failed to read msg (%ld) for chan (%d)\n", rc, chan); |
| return rc; |
| } |
| |
| if(((size_t)rc) < sizeof(gatekeeper_message)) { |
| TLOGE("invalid message of size (%zu) for chan (%d)\n", |
| (size_t)rc, chan); |
| return ERROR_INVALID; |
| } |
| |
| /* get request command */ |
| gatekeeper_message *gk_msg = |
| reinterpret_cast<struct gatekeeper_message *>(msg_buf.get()); |
| |
| UniquePtr<uint8_t[]> out_buf; |
| uint32_t out_buf_size = 0; |
| rc = handle_request(gk_msg->cmd, gk_msg->payload, |
| msg_inf.len - sizeof(gatekeeper_message), &out_buf, &out_buf_size); |
| |
| if (rc < 0) { |
| TLOGE("unable (%ld) to handle request", rc); |
| return send_error_response(chan, gk_msg->cmd, tipc_err_to_gatekeeper_err(rc)); |
| } |
| |
| rc = send_response(chan, gk_msg->cmd, out_buf.get(), out_buf_size); |
| |
| if (rc < 0) { |
| TLOGE("unable (%ld) to send response", rc); |
| } |
| |
| return rc; |
| } |
| |
| static void gatekeeper_handle_port(uevent_t *ev) { |
| if ((ev->event & IPC_HANDLE_POLL_ERROR) || |
| (ev->event & IPC_HANDLE_POLL_HUP) || |
| (ev->event & IPC_HANDLE_POLL_MSG) || |
| (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) { |
| /* should never happen with port handles */ |
| TLOGE("error event (0x%x) for port (%d)\n", |
| ev->event, ev->handle); |
| abort(); |
| } |
| |
| uuid_t peer_uuid; |
| if (ev->event & IPC_HANDLE_POLL_READY) { |
| /* incoming connection: accept it */ |
| int rc = accept(ev->handle, &peer_uuid); |
| if (rc < 0) { |
| TLOGE("failed (%d) to accept on port %d\n", |
| rc, ev->handle); |
| return; |
| } |
| } |
| } |
| |
| static void gatekeeper_handle_channel(uevent_t *ev) { |
| if ((ev->event & IPC_HANDLE_POLL_ERROR) || |
| (ev->event & IPC_HANDLE_POLL_READY)) { |
| /* close it as it is in an error state */ |
| TLOGE("error event (0x%x) for chan (%d)\n", |
| ev->event, ev->handle); |
| abort(); |
| } |
| |
| handle_t chan = ev->handle; |
| |
| if (ev->event & IPC_HANDLE_POLL_MSG) { |
| long rc = handle_msg(chan); |
| if (rc != NO_ERROR) { |
| /* report an error and close channel */ |
| TLOGE("failed (%ld) to handle event on channel %d\n", rc, ev->handle); |
| close(chan); |
| } |
| } |
| |
| if (ev->event & IPC_HANDLE_POLL_HUP) { |
| /* closed by peer. */ |
| close(chan); |
| return; |
| } |
| |
| } |
| |
| static long gatekeeper_ipc_init(void) { |
| int rc; |
| |
| /* Initialize service */ |
| rc = port_create(GATEKEEPER_PORT, 1, GATEKEEPER_MAX_BUFFER_LENGTH, |
| IPC_PORT_ALLOW_NS_CONNECT); |
| if (rc < 0) { |
| TLOGE("Failed (%d) to create port %s\n", rc, GATEKEEPER_PORT); |
| } |
| |
| return rc; |
| } |
| |
| int main(void) { |
| long rc; |
| uevent_t event; |
| |
| TLOGI("Initializing\n"); |
| |
| device = new TrustyGateKeeper(); |
| |
| rc = gatekeeper_ipc_init(); |
| if (rc < 0) { |
| TLOGE("failed (%ld) to initialize gatekeeper", rc); |
| return rc; |
| } |
| |
| handle_t port = (handle_t) rc; |
| |
| /* enter main event loop */ |
| while (true) { |
| event.handle = INVALID_IPC_HANDLE; |
| event.event = 0; |
| event.cookie = NULL; |
| |
| rc = wait_any(&event, -1); |
| if (rc < 0) { |
| TLOGE("wait_any failed (%ld)\n", rc); |
| break; |
| } |
| |
| if (rc == NO_ERROR) { /* got an event */ |
| if (event.handle == port) { |
| gatekeeper_handle_port(&event); |
| } else { |
| gatekeeper_handle_channel(&event); |
| } |
| } |
| } |
| |
| return 0; |
| } |