diff options
Diffstat (limited to 'ipc/gatekeeper_ipc.cpp')
-rw-r--r-- | ipc/gatekeeper_ipc.cpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/ipc/gatekeeper_ipc.cpp b/ipc/gatekeeper_ipc.cpp new file mode 100644 index 0000000..267a629 --- /dev/null +++ b/ipc/gatekeeper_ipc.cpp @@ -0,0 +1,329 @@ +/* + * 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 (%d) 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 (%d) 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 (%d)\n", rc, chan); + return rc; + } + + if(((unsigned long) rc) < sizeof(gatekeeper_message)) { + TLOGE("invalid message of size (%d)\n", 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 (%d) 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 (%d) 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 (%d) 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 (%d) 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 (%d)\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; +} |