summaryrefslogtreecommitdiff
path: root/ipc/gatekeeper_ipc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/gatekeeper_ipc.cpp')
-rw-r--r--ipc/gatekeeper_ipc.cpp329
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;
+}