trunks: Add tpm simulator handle.

Add a handle so trunks can talk to a TPM simulator.

TEST=$ sudo tpm-simulator  // in one terminal
     $ sudo trunksd --simulator // in another terminal
     run trunks_client in another terminal. --startup, --clear,
     and --status all success. --own --owner_password="" fails
     during salting key generation.
BUG=none

Change-Id: I815bf7cf11d6def69b7a5600dbb509b6d231ce19
Signed-off-by: Jocelyn Bohr <bohr@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/293570
Reviewed-by: Utkarsh Sanghi <usanghi@chromium.org>
diff --git a/tpm_simulator_handle.cc b/tpm_simulator_handle.cc
new file mode 100644
index 0000000..cae6b48
--- /dev/null
+++ b/tpm_simulator_handle.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "trunks/tpm_simulator_handle.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <base/callback.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+namespace {
+
+const char kTpmSimRequestFile[] = "/dev/tpm-req";
+const char kTpmSimResponseFile[] = "/dev/tpm-resp";
+const uint32_t kTpmBufferSize = 4096;
+const int kInvalidFileDescriptor = -1;
+
+}  // namespace
+
+namespace trunks {
+
+TpmSimulatorHandle::TpmSimulatorHandle() :
+    req_fd_(kInvalidFileDescriptor), resp_fd_(kInvalidFileDescriptor) {}
+
+TpmSimulatorHandle::~TpmSimulatorHandle() {
+  int result = IGNORE_EINTR(close(req_fd_));
+  if (result == -1) {
+    PLOG(ERROR) << "TPM: couldn't close " << kTpmSimRequestFile;
+  } else {
+    LOG(INFO) << "TPM: " << kTpmSimRequestFile << " closed successfully";
+  }
+  result = IGNORE_EINTR(close(resp_fd_));
+  if (result == -1) {
+    PLOG(ERROR) << "TPM: couldn't close " << kTpmSimResponseFile;
+  } else {
+    LOG(INFO) << "TPM: " << kTpmSimResponseFile << " closed successfully";
+  }
+}
+
+bool TpmSimulatorHandle::Init() {
+  if (req_fd_ == kInvalidFileDescriptor) {
+    req_fd_ = HANDLE_EINTR(open("/dev/tpm-req", O_RDWR));
+    if (req_fd_ == kInvalidFileDescriptor) {
+      PLOG(ERROR) << "TPM: Error opening file descriptor at "
+                  << kTpmSimRequestFile;
+      return false;
+    }
+    LOG(INFO) << "TPM: " << kTpmSimRequestFile << " opened successfully";
+  }
+  if (resp_fd_ == kInvalidFileDescriptor) {
+    resp_fd_ = HANDLE_EINTR(open("/dev/tpm-resp", O_RDWR));
+    if (resp_fd_ == kInvalidFileDescriptor) {
+      PLOG(ERROR) << "TPM: Error opening file descriptor at "
+                  << kTpmSimResponseFile;
+      return false;
+    }
+    LOG(INFO) << "TPM: " << kTpmSimResponseFile << " opened successfully";
+  }
+  return true;
+}
+
+void TpmSimulatorHandle::SendCommand(const std::string& command,
+                            const ResponseCallback& callback) {
+  callback.Run(SendCommandAndWait(command));
+}
+
+std::string TpmSimulatorHandle::SendCommandAndWait(const std::string& command) {
+  std::string response;
+  TPM_RC result = SendCommandInternal(command, &response);
+  if (result != TPM_RC_SUCCESS) {
+    response = CreateErrorResponse(result);
+  }
+  return response;
+}
+
+TPM_RC TpmSimulatorHandle::SendCommandInternal(const std::string& command,
+                                      std::string* response) {
+  CHECK_NE(req_fd_, kInvalidFileDescriptor);
+  CHECK_NE(resp_fd_, kInvalidFileDescriptor);
+  int result = HANDLE_EINTR(write(req_fd_, command.data(), command.length()));
+  if (result < 0) {
+    PLOG(ERROR) << "TPM: Error writing to TPM simulator request handle.";
+    return TRUNKS_RC_WRITE_ERROR;
+  }
+  if (static_cast<size_t>(result) != command.length()) {
+    LOG(ERROR) << "TPM: Error writing to TPM simulator request handle: "
+               << result << " vs " << command.length();
+    return TRUNKS_RC_WRITE_ERROR;
+  }
+  char response_buf[kTpmBufferSize];
+  result = HANDLE_EINTR(read(resp_fd_, response_buf, kTpmBufferSize));
+  if (result < 0) {
+    PLOG(ERROR) << "TPM: Error reading from TPM simulator response handle.";
+    return TRUNKS_RC_READ_ERROR;
+  }
+  response->assign(response_buf, static_cast<size_t>(result));
+  return TPM_RC_SUCCESS;
+}
+
+}  // namespace trunks
diff --git a/tpm_simulator_handle.h b/tpm_simulator_handle.h
new file mode 100644
index 0000000..bf406ec
--- /dev/null
+++ b/tpm_simulator_handle.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TRUNKS_TPM_SIMULATOR_HANDLE_H_
+#define TRUNKS_TPM_SIMULATOR_HANDLE_H_
+
+#include "trunks/command_transceiver.h"
+
+#include <string>
+
+#include "trunks/error_codes.h"
+
+namespace trunks {
+
+// Sends command requests to a software TPM via a handle to /dev/tpm-req.
+// Receives command responses via a handle to /dev/tpm-resp. All commands are
+// sent synchronously. The SendCommand method is supported but does not return
+// until a response is received and the callback has been called. Command and
+// response data are opaque to this class; it performs no validation.
+//
+// Example:
+//   TpmSimulatorHandle handle;
+//   if (!handle.Init()) {...}
+//   std::string response = handle.SendCommandAndWait(command);
+class TpmSimulatorHandle : public CommandTransceiver  {
+ public:
+  TpmSimulatorHandle();
+  ~TpmSimulatorHandle() override;
+
+  // Initializes a TpmSimulatorHandle instance. This method must be called
+  // successfully before any other method. Returns true on success.
+  bool Init() override;
+
+  // CommandTranceiver methods.
+  void SendCommand(const std::string& command,
+                   const ResponseCallback& callback) override;
+  std::string SendCommandAndWait(const std::string& command) override;
+
+ private:
+  // Writes a |command| to /dev/tpm-req and reads the |response| from
+  // /dev/tpm-resp. Returns TPM_RC_SUCCESS on success.
+  TPM_RC SendCommandInternal(const std::string& command, std::string* response);
+
+  int req_fd_;  // A file descriptor for /dev/tpm-req.
+  int resp_fd_;  // A file descriptor for /dev/tpm-resp.
+
+  DISALLOW_COPY_AND_ASSIGN(TpmSimulatorHandle);
+};
+
+}  // namespace trunks
+
+#endif  // TRUNKS_TPM_SIMULATOR_HANDLE_H_
diff --git a/trunks.gyp b/trunks.gyp
index 0084ac7..a352565 100644
--- a/trunks.gyp
+++ b/trunks.gyp
@@ -83,6 +83,7 @@
         'background_command_transceiver.cc',
         'resource_manager.cc',
         'tpm_handle.cc',
+        'tpm_simulator_handle.cc',
         'trunks_service.cc',
       ],
       'dependencies': [
diff --git a/trunksd.cc b/trunksd.cc
index f825177..106f6fa 100644
--- a/trunksd.cc
+++ b/trunksd.cc
@@ -18,6 +18,7 @@
 #include "trunks/dbus_interface.h"
 #include "trunks/resource_manager.h"
 #include "trunks/tpm_handle.h"
+#include "trunks/tpm_simulator_handle.h"
 #include "trunks/trunks_factory_impl.h"
 #include "trunks/trunks_ftdi_spi.h"
 #include "trunks/trunks_service.h"
@@ -107,6 +108,8 @@
   trunks::CommandTransceiver *transceiver;
   if (cl->HasSwitch("ftdi")) {
     transceiver = new trunks::TrunksFtdiSpi();
+  } else if (cl->HasSwitch("simulator")) {
+    transceiver = new trunks::TpmSimulatorHandle();
   } else {
     transceiver = new trunks::TpmHandle();
   }