1//
2// Copyright (C) 2015 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "tpm_manager/server/tpm_connection.h"
18
19#include <base/logging.h>
20#include <base/stl_util.h>
21#include <base/threading/platform_thread.h>
22#include <base/time/time.h>
23#include <trousers/tss.h>
24#include <trousers/trousers.h>  // NOLINT(build/include_alpha)
25
26#include "tpm_manager/server/tpm_util.h"
27
28namespace {
29
30const int kTpmConnectRetries = 10;
31const int kTpmConnectIntervalMs = 100;
32
33}  // namespace
34
35namespace tpm_manager {
36
37TSS_HCONTEXT TpmConnection::GetContext() {
38  if (!ConnectContextIfNeeded()) {
39    return 0;
40  }
41  return context_.value();
42}
43
44TSS_HTPM TpmConnection::GetTpm() {
45  if (!ConnectContextIfNeeded()) {
46    return 0;
47  }
48  TSS_RESULT result;
49  TSS_HTPM tpm_handle;
50  if (TPM_ERROR(result = Tspi_Context_GetTpmObject(context_.value(),
51                                                   &tpm_handle))) {
52    TPM_LOG(ERROR, result) << "Error getting a handle to the TPM.";
53    return 0;
54  }
55  return tpm_handle;
56}
57
58TSS_HTPM TpmConnection::GetTpmWithAuth(const std::string& owner_password) {
59  TSS_HTPM tpm_handle = GetTpm();
60  if (tpm_handle == 0) {
61    return 0;
62  }
63  TSS_RESULT result;
64  TSS_HPOLICY tpm_usage_policy;
65  if (TPM_ERROR(result = Tspi_GetPolicyObject(tpm_handle,
66                                              TSS_POLICY_USAGE,
67                                              &tpm_usage_policy))) {
68    TPM_LOG(ERROR, result) << "Error calling Tspi_GetPolicyObject";
69    return false;
70  }
71  if (TPM_ERROR(result = Tspi_Policy_SetSecret(
72      tpm_usage_policy,
73      TSS_SECRET_MODE_PLAIN,
74      owner_password.size(),
75      reinterpret_cast<BYTE *>(const_cast<char*>(owner_password.data()))))) {
76    TPM_LOG(ERROR, result) << "Error calling Tspi_Policy_SetSecret";
77    return false;
78  }
79  return tpm_handle;
80}
81
82bool TpmConnection::ConnectContextIfNeeded() {
83  if (context_.value() != 0) {
84    return true;
85  }
86  TSS_RESULT result;
87  if (TPM_ERROR(result = Tspi_Context_Create(context_.ptr()))) {
88    TPM_LOG(ERROR, result) << "Error connecting to TPM.";
89    return false;
90  }
91  // We retry on failure. It might be that tcsd is starting up.
92  for (int i = 0; i < kTpmConnectRetries; i++) {
93    if (TPM_ERROR(result = Tspi_Context_Connect(context_, nullptr))) {
94      if (ERROR_CODE(result) == TSS_E_COMM_FAILURE) {
95        base::PlatformThread::Sleep(
96            base::TimeDelta::FromMilliseconds(kTpmConnectIntervalMs));
97      } else {
98        TPM_LOG(ERROR, result) << "Error connecting to TPM.";
99        return false;
100      }
101    } else {
102      break;
103    }
104  }
105  return (context_.value() != 0);
106}
107
108}  // namespace tpm_manager
109