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 "trunks/resource_manager.h"
18
19#include <algorithm>
20#include <map>
21#include <set>
22#include <string>
23#include <vector>
24
25#include <base/callback.h>
26
27#include "trunks/error_codes.h"
28
29namespace {
30
31const int kMaxCommandAttempts = 3;
32const size_t kMinimumAuthorizationSize = 9;
33const size_t kMessageHeaderSize = 10;
34const trunks::TPM_HANDLE kMaxVirtualHandle =
35    (trunks::HR_TRANSIENT + trunks::HR_HANDLE_MASK);
36
37class ScopedBool {
38 public:
39  ScopedBool() : target_(nullptr) {}
40  ~ScopedBool() {
41    if (target_) {
42      *target_ = false;
43    }
44  }
45  void Enable(bool* target) {
46    target_ = target;
47    *target_ = true;
48  }
49
50 private:
51  bool* target_;
52};
53
54}  // namespace
55
56namespace trunks {
57
58ResourceManager::ResourceManager(const TrunksFactory& factory,
59                                 CommandTransceiver* next_transceiver)
60    : factory_(factory), next_transceiver_(next_transceiver) {}
61
62ResourceManager::~ResourceManager() {}
63
64void ResourceManager::Initialize() {
65  // Abort if the TPM is not in a reasonable state and we can't get it into one.
66  std::unique_ptr<TpmUtility> tpm_utility = factory_.GetTpmUtility();
67  CHECK_EQ(tpm_utility->Startup(), TPM_RC_SUCCESS);
68  CHECK_EQ(tpm_utility->InitializeTpm(), TPM_RC_SUCCESS);
69  // Full control of the TPM is assumed and required. Existing transient object
70  // and session handles are mercilessly flushed.
71  for (UINT32 handle_type :
72       {HR_TRANSIENT, HR_HMAC_SESSION, HR_POLICY_SESSION}) {
73    TPMI_YES_NO more_data = YES;
74    TPMS_CAPABILITY_DATA data;
75    UINT32 handle_range = handle_type;
76    while (more_data) {
77      TPM_RC result = factory_.GetTpm()->GetCapabilitySync(
78          TPM_CAP_HANDLES, handle_range, MAX_CAP_HANDLES, &more_data, &data,
79          nullptr);
80      if (result != TPM_RC_SUCCESS) {
81        LOG(WARNING) << "Failed to query existing handles: "
82                     << GetErrorString(result);
83        break;
84      }
85      const TPML_HANDLE& handle_list = data.data.handles;
86      for (UINT32 i = 0; i < handle_list.count; ++i) {
87        factory_.GetTpm()->FlushContextSync(handle_list.handle[i], nullptr);
88      }
89      if (more_data) {
90        // Adjust the range to be greater than the most recent handle so on the
91        // next query we'll start where we left off.
92        handle_range = handle_list.handle[handle_list.count - 1];
93      }
94    }
95  }
96}
97
98void ResourceManager::SendCommand(const std::string& command,
99                                  const ResponseCallback& callback) {
100  callback.Run(SendCommandAndWait(command));
101}
102
103std::string ResourceManager::SendCommandAndWait(const std::string& command) {
104  // Sanitize the |command|. If this succeeds consistency of the command header
105  // and the size of all other sections can be assumed.
106  MessageInfo command_info;
107  TPM_RC result = ParseCommand(command, &command_info);
108  if (result != TPM_RC_SUCCESS) {
109    return CreateErrorResponse(result);
110  }
111  // A special case for FlushContext. It requires special handling because it
112  // has a handle as a parameter and because we need to cleanup if it succeeds.
113  if (command_info.code == TPM_CC_FlushContext) {
114    return ProcessFlushContext(command, command_info);
115  }
116  // Process all the input handles, e.g. map virtual handles.
117  std::vector<TPM_HANDLE> updated_handles;
118  for (auto handle : command_info.handles) {
119    TPM_HANDLE tpm_handle;
120    result = ProcessInputHandle(command_info, handle, &tpm_handle);
121    if (result != TPM_RC_SUCCESS) {
122      return CreateErrorResponse(result);
123    }
124    updated_handles.push_back(tpm_handle);
125  }
126  std::string updated_command = ReplaceHandles(command, updated_handles);
127  // Make sure all the required sessions are loaded.
128  for (auto handle : command_info.session_handles) {
129    result = EnsureSessionIsLoaded(command_info, handle);
130    if (result != TPM_RC_SUCCESS) {
131      return CreateErrorResponse(result);
132    }
133  }
134  // On a ContextLoad we may need to map virtualized context data.
135  if (command_info.code == TPM_CC_ContextLoad) {
136    std::string actual_load_data =
137        GetActualContextFromExternalContext(command_info.parameter_data);
138    // Check equality to see if replacement is necessary, and check size to see
139    // if the command looks like we expect (the idea is to avoid 'fixing'
140    // malformed commands). Note: updated_command.size() is guaranteed to be >=
141    // kMessageHeaderSize based on the sanitization in ParseCommand.
142    if (actual_load_data != command_info.parameter_data &&
143        actual_load_data.size() ==
144            updated_command.size() - kMessageHeaderSize) {
145      // Replace the parameter section of the command with |actual_load_data|.
146      VLOG(1) << "REPLACE_EXTERNAL_CONTEXT";
147      updated_command.replace(kMessageHeaderSize, std::string::npos,
148                              actual_load_data);
149    }
150  }
151  // Send the |updated_command| to the next layer. Attempt to fix any actionable
152  // warnings for up to kMaxCommandAttempts.
153  std::string response;
154  MessageInfo response_info;
155  int attempts = 0;
156  while (attempts++ < kMaxCommandAttempts) {
157    response = next_transceiver_->SendCommandAndWait(updated_command);
158    result = ParseResponse(command_info, response, &response_info);
159    if (result != TPM_RC_SUCCESS) {
160      return CreateErrorResponse(result);
161    }
162    if (!FixWarnings(command_info, response_info.code)) {
163      // No actionable warnings were handled.
164      break;
165    }
166  }
167  if (response_info.code == TPM_RC_SUCCESS) {
168    if (response_info.session_continued.size() !=
169        command_info.session_handles.size()) {
170      LOG(WARNING) << "Session count mismatch!";
171    }
172    // Cleanup any sessions that were not continued.
173    for (size_t i = 0; i < command_info.session_handles.size(); ++i) {
174      if (i < response_info.session_continued.size() &&
175          !response_info.session_continued[i]) {
176        CleanupFlushedHandle(command_info.session_handles[i]);
177      }
178    }
179    // On a successful context save we need to cache the context data in case it
180    // needs to be virtualized later.
181    if (command_info.code == TPM_CC_ContextSave) {
182      ProcessExternalContextSave(command_info, response_info);
183    }
184    // Process all the output handles, which is loosely the inverse of the input
185    // handle processing. E.g. virtualize handles.
186    std::vector<TPM_HANDLE> virtual_handles;
187    for (auto handle : response_info.handles) {
188      virtual_handles.push_back(ProcessOutputHandle(handle));
189    }
190    response = ReplaceHandles(response, virtual_handles);
191  }
192  return response;
193}
194
195bool ResourceManager::ChooseSessionToEvict(
196    const std::vector<TPM_HANDLE>& sessions_to_retain,
197    TPM_HANDLE* session_to_evict) {
198  // Build a list of candidates by excluding |sessions_to_retain|.
199  std::vector<TPM_HANDLE> candidates;
200  for (auto& item : session_handles_) {
201    HandleInfo& info = item.second;
202    if (info.is_loaded &&
203        std::find(sessions_to_retain.begin(), sessions_to_retain.end(),
204                  info.tpm_handle) == sessions_to_retain.end()) {
205      candidates.push_back(item.first);
206    }
207  }
208  if (candidates.empty()) {
209    LOG(WARNING) << "No sessions to evict.";
210    return false;
211  }
212  // Choose the candidate with the earliest |time_of_last_use|.
213  auto oldest_iter = std::min_element(
214      candidates.begin(), candidates.end(), [this](TPM_HANDLE a, TPM_HANDLE b) {
215        return (session_handles_[a].time_of_last_use <
216                session_handles_[b].time_of_last_use);
217      });
218  *session_to_evict = *oldest_iter;
219  return true;
220}
221
222void ResourceManager::CleanupFlushedHandle(TPM_HANDLE flushed_handle) {
223  if (IsObjectHandle(flushed_handle)) {
224    // For transient object handles, remove both the actual and virtual handles.
225    if (virtual_object_handles_.count(flushed_handle) > 0) {
226      tpm_object_handles_.erase(
227          virtual_object_handles_[flushed_handle].tpm_handle);
228      virtual_object_handles_.erase(flushed_handle);
229    }
230  } else if (IsSessionHandle(flushed_handle)) {
231    auto iter = session_handles_.find(flushed_handle);
232    if (iter == session_handles_.end()) {
233      return;
234    }
235    // For session handles, remove the handle and any associated context data.
236    HandleInfo& info = iter->second;
237    if (!info.is_loaded) {
238      std::string actual_context_data;
239      Serialize_TPMS_CONTEXT(info.context, &actual_context_data);
240      if (actual_context_to_external_.count(actual_context_data) > 0) {
241        external_context_to_actual_.erase(
242            actual_context_to_external_[actual_context_data]);
243        actual_context_to_external_.erase(actual_context_data);
244      }
245    }
246    session_handles_.erase(flushed_handle);
247    VLOG(1) << "CLEANUP_SESSION: " << std::hex << flushed_handle;
248  }
249}
250
251TPM_HANDLE ResourceManager::CreateVirtualHandle() {
252  TPM_HANDLE handle;
253  do {
254    handle = next_virtual_handle_;
255    if (next_virtual_handle_ == kMaxVirtualHandle) {
256      next_virtual_handle_ = TRANSIENT_FIRST;
257    } else {
258      ++next_virtual_handle_;
259    }
260  } while (virtual_object_handles_.count(handle) > 0);
261  return handle;
262}
263
264TPM_RC ResourceManager::EnsureSessionIsLoaded(const MessageInfo& command_info,
265                                              TPM_HANDLE session_handle) {
266  // A password authorization can skip all this.
267  if (session_handle == TPM_RS_PW) {
268    return TPM_RC_SUCCESS;
269  }
270  auto handle_iter = session_handles_.find(session_handle);
271  if (handle_iter == session_handles_.end()) {
272    return MakeError(TPM_RC_HANDLE, FROM_HERE);
273  }
274  HandleInfo& handle_info = handle_iter->second;
275  if (!handle_info.is_loaded) {
276    TPM_RC result = LoadContext(command_info, &handle_info);
277    if (result != TPM_RC_SUCCESS) {
278      return result;
279    }
280    VLOG(1) << "RELOAD_SESSION: " << std::hex << session_handle;
281  }
282  handle_info.time_of_last_use = base::TimeTicks::Now();
283  return TPM_RC_SUCCESS;
284}
285
286void ResourceManager::EvictObjects(const MessageInfo& command_info) {
287  for (auto& item : virtual_object_handles_) {
288    HandleInfo& info = item.second;
289    if (!info.is_loaded ||
290        std::find(command_info.handles.begin(), command_info.handles.end(),
291                  item.first) != command_info.handles.end()) {
292      continue;
293    }
294    TPM_RC result = SaveContext(command_info, &info);
295    if (result != TPM_RC_SUCCESS) {
296      LOG(WARNING) << "Failed to save transient object: "
297                   << GetErrorString(result);
298      continue;
299    }
300    result = factory_.GetTpm()->FlushContextSync(info.tpm_handle, nullptr);
301    if (result != TPM_RC_SUCCESS) {
302      LOG(WARNING) << "Failed to evict transient object: "
303                   << GetErrorString(result);
304      continue;
305    }
306    tpm_object_handles_.erase(info.tpm_handle);
307    VLOG(1) << "EVICT_OBJECT: " << std::hex << info.tpm_handle;
308  }
309}
310
311void ResourceManager::EvictSession(const MessageInfo& command_info) {
312  TPM_HANDLE session_to_evict;
313  if (!ChooseSessionToEvict(command_info.session_handles, &session_to_evict)) {
314    return;
315  }
316  HandleInfo& info = session_handles_[session_to_evict];
317  TPM_RC result = SaveContext(command_info, &info);
318  if (result != TPM_RC_SUCCESS) {
319    LOG(WARNING) << "Failed to evict session: " << GetErrorString(result);
320  }
321  VLOG(1) << "EVICT_SESSION: " << std::hex << session_to_evict;
322}
323
324std::vector<TPM_HANDLE> ResourceManager::ExtractHandlesFromBuffer(
325    size_t number_of_handles,
326    std::string* buffer) {
327  std::vector<TPM_HANDLE> handles(number_of_handles);
328  for (size_t i = 0; i < number_of_handles; ++i) {
329    TPM_HANDLE handle;
330    if (TPM_RC_SUCCESS == Parse_TPM_HANDLE(buffer, &handle, nullptr)) {
331      handles[i] = handle;
332    }
333  }
334  return handles;
335}
336
337void ResourceManager::FixContextGap(const MessageInfo& command_info) {
338  std::vector<TPM_HANDLE> sessions_to_ungap;
339  for (const auto& item : session_handles_) {
340    const HandleInfo& info = item.second;
341    if (!info.is_loaded) {
342      sessions_to_ungap.push_back(item.first);
343    }
344  }
345  // Sort by |time_of_create|.
346  std::sort(sessions_to_ungap.begin(), sessions_to_ungap.end(),
347            [this](TPM_HANDLE a, TPM_HANDLE b) {
348              return (session_handles_[a].time_of_create <
349                      session_handles_[b].time_of_create);
350            });
351  for (auto handle : sessions_to_ungap) {
352    HandleInfo& info = session_handles_[handle];
353    // Loading and re-saving allows the TPM to assign a new context counter.
354    std::string old_context_blob;
355    Serialize_TPMS_CONTEXT(info.context, &old_context_blob);
356    TPM_RC result = LoadContext(command_info, &info);
357    if (result != TPM_RC_SUCCESS) {
358      LOG(WARNING) << "Failed to un-gap session (load): "
359                   << GetErrorString(result);
360      continue;
361    }
362    result = SaveContext(command_info, &info);
363    if (result != TPM_RC_SUCCESS) {
364      LOG(WARNING) << "Failed to un-gap session (save): "
365                   << GetErrorString(result);
366      continue;
367    }
368    // If this context is one that we're tracking for external use, update it.
369    auto iter = actual_context_to_external_.find(old_context_blob);
370    if (iter == actual_context_to_external_.end()) {
371      continue;
372    }
373    std::string new_context_blob;
374    Serialize_TPMS_CONTEXT(info.context, &new_context_blob);
375    const std::string& external_context_blob = iter->second;
376    actual_context_to_external_[new_context_blob] = external_context_blob;
377    external_context_to_actual_[external_context_blob] = new_context_blob;
378    actual_context_to_external_.erase(old_context_blob);
379  }
380}
381
382bool ResourceManager::FixWarnings(const MessageInfo& command_info,
383                                  TPM_RC result) {
384  if ((result & RC_WARN) == 0) {
385    return false;
386  }
387  // This method can be called anytime without tracking whether the current
388  // operation is already an attempt to fix a warning. All re-entrance issues
389  // are dealt with here using the following rule: Never attempt to fix the same
390  // warning twice.
391  ScopedBool scoped_bool;
392  if (!fixing_warnings_) {
393    scoped_bool.Enable(&fixing_warnings_);
394    warnings_already_seen_.clear();
395  } else if (warnings_already_seen_.count(result) > 0) {
396    return false;
397  }
398  warnings_already_seen_.insert(result);
399  switch (result) {
400    case TPM_RC_CONTEXT_GAP:
401      FixContextGap(command_info);
402      return true;
403    case TPM_RC_OBJECT_MEMORY:
404    case TPM_RC_OBJECT_HANDLES:
405      EvictObjects(command_info);
406      return true;
407    case TPM_RC_SESSION_MEMORY:
408      EvictSession(command_info);
409      return true;
410    case TPM_RC_MEMORY:
411      EvictObjects(command_info);
412      EvictSession(command_info);
413      return true;
414    case TPM_RC_SESSION_HANDLES:
415      FlushSession(command_info);
416      return true;
417  }
418  return false;
419}
420
421void ResourceManager::FlushSession(const MessageInfo& command_info) {
422  TPM_HANDLE session_to_flush;
423  LOG(WARNING) << "Resource manager needs to flush a session.";
424  if (!ChooseSessionToEvict(command_info.session_handles, &session_to_flush)) {
425    return;
426  }
427  TPM_RC result =
428      factory_.GetTpm()->FlushContextSync(session_to_flush, nullptr);
429  if (result != TPM_RC_SUCCESS) {
430    LOG(WARNING) << "Failed to flush session: " << GetErrorString(result);
431    return;
432  }
433  CleanupFlushedHandle(session_to_flush);
434}
435
436std::string ResourceManager::GetActualContextFromExternalContext(
437    const std::string& external_context) {
438  auto iter = external_context_to_actual_.find(external_context);
439  if (iter == external_context_to_actual_.end()) {
440    return external_context;
441  }
442  return iter->second;
443}
444
445bool ResourceManager::IsObjectHandle(TPM_HANDLE handle) const {
446  return ((handle & HR_RANGE_MASK) == HR_TRANSIENT);
447}
448
449bool ResourceManager::IsSessionHandle(TPM_HANDLE handle) const {
450  return ((handle & HR_RANGE_MASK) == HR_HMAC_SESSION ||
451          (handle & HR_RANGE_MASK) == HR_POLICY_SESSION);
452}
453
454TPM_RC ResourceManager::LoadContext(const MessageInfo& command_info,
455                                    HandleInfo* handle_info) {
456  CHECK(!handle_info->is_loaded);
457  TPM_RC result = TPM_RC_SUCCESS;
458  int attempts = 0;
459  while (attempts++ < kMaxCommandAttempts) {
460    result = factory_.GetTpm()->ContextLoadSync(
461        handle_info->context, &handle_info->tpm_handle, nullptr);
462    if (!FixWarnings(command_info, result)) {
463      break;
464    }
465  }
466  if (result != TPM_RC_SUCCESS) {
467    LOG(ERROR) << __func__
468               << ": Failed to load context: " << GetErrorString(result);
469    return result;
470  }
471  handle_info->is_loaded = true;
472  return result;
473}
474
475TPM_RC ResourceManager::MakeError(TPM_RC tpm_error,
476                                  const ::tracked_objects::Location& location) {
477  LOG(ERROR) << "ResourceManager::" << location.function_name() << ":"
478             << location.line_number() << ": " << GetErrorString(tpm_error);
479  return tpm_error + kResourceManagerTpmErrorBase;
480}
481
482TPM_RC ResourceManager::ParseCommand(const std::string& command,
483                                     MessageInfo* command_info) {
484  CHECK(command_info);
485  std::string buffer = command;
486  TPM_ST tag;
487  TPM_RC result = Parse_TPM_ST(&buffer, &tag, nullptr);
488  if (result != TPM_RC_SUCCESS) {
489    return MakeError(result, FROM_HERE);
490  }
491  if (tag != TPM_ST_SESSIONS && tag != TPM_ST_NO_SESSIONS) {
492    return MakeError(TPM_RC_TAG, FROM_HERE);
493  }
494  command_info->has_sessions = (tag == TPM_ST_SESSIONS);
495
496  UINT32 size = 0;
497  result = Parse_UINT32(&buffer, &size, nullptr);
498  if (result != TPM_RC_SUCCESS) {
499    return MakeError(result, FROM_HERE);
500  }
501  if (size != command.size()) {
502    return MakeError(TPM_RC_SIZE, FROM_HERE);
503  }
504
505  result = Parse_TPM_CC(&buffer, &command_info->code, nullptr);
506  if (result != TPM_RC_SUCCESS) {
507    return MakeError(result, FROM_HERE);
508  }
509  if (command_info->code < TPM_CC_FIRST || command_info->code > TPM_CC_LAST) {
510    return MakeError(TPM_RC_COMMAND_CODE, FROM_HERE);
511  }
512
513  size_t number_of_handles = GetNumberOfRequestHandles(command_info->code);
514  command_info->handles = ExtractHandlesFromBuffer(number_of_handles, &buffer);
515  if (number_of_handles != command_info->handles.size()) {
516    return MakeError(TPM_RC_SIZE, FROM_HERE);
517  }
518  if (command_info->has_sessions) {
519    // Sessions exist, so we're expecting a valid authorization size value.
520    UINT32 authorization_size = 0;
521    result = Parse_UINT32(&buffer, &authorization_size, nullptr);
522    if (result != TPM_RC_SUCCESS) {
523      return MakeError(result, FROM_HERE);
524    }
525    if (buffer.size() < authorization_size ||
526        authorization_size < kMinimumAuthorizationSize) {
527      return MakeError(TPM_RC_SIZE, FROM_HERE);
528    }
529    // Move out the parameter bytes, leaving only the authorization section.
530    command_info->parameter_data = buffer.substr(authorization_size);
531    buffer.erase(authorization_size);
532    // Parse as many authorization sessions as there are in the section.
533    while (!buffer.empty()) {
534      TPM_HANDLE handle;
535      result = Parse_TPM_HANDLE(&buffer, &handle, nullptr);
536      if (result != TPM_RC_SUCCESS) {
537        return MakeError(result, FROM_HERE);
538      }
539      if (handle != TPM_RS_PW && session_handles_.count(handle) == 0) {
540        return MakeError(TPM_RC_HANDLE, FROM_HERE);
541      }
542      TPM2B_NONCE nonce;
543      result = Parse_TPM2B_NONCE(&buffer, &nonce, nullptr);
544      if (result != TPM_RC_SUCCESS) {
545        return MakeError(result, FROM_HERE);
546      }
547      BYTE attributes;
548      result = Parse_BYTE(&buffer, &attributes, nullptr);
549      if (result != TPM_RC_SUCCESS) {
550        return MakeError(result, FROM_HERE);
551      }
552      TPM2B_DIGEST authorization;
553      result = Parse_TPM2B_DIGEST(&buffer, &authorization, nullptr);
554      if (result != TPM_RC_SUCCESS) {
555        return MakeError(result, FROM_HERE);
556      }
557      command_info->session_handles.push_back(handle);
558      command_info->session_continued.push_back((attributes & 1) == 1);
559    }
560  } else {
561    // No sessions, so all remaining data is parameter data.
562    command_info->parameter_data = buffer;
563  }
564  return TPM_RC_SUCCESS;
565}
566
567TPM_RC ResourceManager::ParseResponse(const MessageInfo& command_info,
568                                      const std::string& response,
569                                      MessageInfo* response_info) {
570  CHECK(response_info);
571  std::string buffer = response;
572  TPM_ST tag;
573  TPM_RC result = Parse_TPM_ST(&buffer, &tag, nullptr);
574  if (result != TPM_RC_SUCCESS) {
575    return MakeError(result, FROM_HERE);
576  }
577  if (tag != TPM_ST_SESSIONS && tag != TPM_ST_NO_SESSIONS) {
578    return MakeError(TPM_RC_TAG, FROM_HERE);
579  }
580  response_info->has_sessions = (tag == TPM_ST_SESSIONS);
581
582  UINT32 size = 0;
583  result = Parse_UINT32(&buffer, &size, nullptr);
584  if (result != TPM_RC_SUCCESS) {
585    return MakeError(result, FROM_HERE);
586  }
587  if (size != response.size()) {
588    return MakeError(TPM_RC_SIZE, FROM_HERE);
589  }
590
591  result = Parse_TPM_RC(&buffer, &response_info->code, nullptr);
592  if (result != TPM_RC_SUCCESS) {
593    return MakeError(result, FROM_HERE);
594  }
595
596  size_t number_of_handles = GetNumberOfResponseHandles(command_info.code);
597  response_info->handles = ExtractHandlesFromBuffer(number_of_handles, &buffer);
598  if (number_of_handles != response_info->handles.size()) {
599    return MakeError(TPM_RC_SIZE, FROM_HERE);
600  }
601  if (response_info->has_sessions) {
602    // Sessions exist, so we're expecting a valid parameter size value.
603    UINT32 parameter_size = 0;
604    result = Parse_UINT32(&buffer, &parameter_size, nullptr);
605    if (result != TPM_RC_SUCCESS) {
606      return MakeError(result, FROM_HERE);
607    }
608    if (buffer.size() < parameter_size) {
609      return MakeError(TPM_RC_SIZE, FROM_HERE);
610    }
611    // Move out the parameter bytes, leaving only the authorization section.
612    response_info->parameter_data = buffer.substr(0, parameter_size);
613    buffer.erase(0, parameter_size);
614    // Parse as many authorization sessions as there are in the section.
615    while (!buffer.empty()) {
616      TPM2B_NONCE nonce;
617      result = Parse_TPM2B_NONCE(&buffer, &nonce, nullptr);
618      if (result != TPM_RC_SUCCESS) {
619        return MakeError(result, FROM_HERE);
620      }
621      BYTE attributes;
622      result = Parse_BYTE(&buffer, &attributes, nullptr);
623      if (result != TPM_RC_SUCCESS) {
624        return MakeError(result, FROM_HERE);
625      }
626      TPM2B_DIGEST acknowledgement;
627      result = Parse_TPM2B_DIGEST(&buffer, &acknowledgement, nullptr);
628      if (result != TPM_RC_SUCCESS) {
629        return MakeError(result, FROM_HERE);
630      }
631      response_info->session_continued.push_back((attributes & 1) == 1);
632    }
633  } else {
634    // No sessions, so all remaining data is parameter data.
635    response_info->parameter_data = buffer;
636  }
637  return TPM_RC_SUCCESS;
638}
639
640void ResourceManager::ProcessExternalContextSave(
641    const MessageInfo& command_info,
642    const MessageInfo& response_info) {
643  CHECK_EQ(command_info.code, TPM_CC_ContextSave);
644  if (command_info.handles.size() != 1) {
645    LOG(WARNING) << "Invalid context save command.";
646    return;
647  }
648  // We know command_info.handles[0] is valid because this is validated when the
649  // command is parsed.
650  TPM_HANDLE saved_handle = command_info.handles[0];
651  // Only track external context data for session handles.
652  if (!IsSessionHandle(saved_handle)) {
653    return;
654  }
655  std::string mutable_parameter = response_info.parameter_data;
656  TPMS_CONTEXT context;
657  std::string context_blob;
658  TPM_RC result =
659      Parse_TPMS_CONTEXT(&mutable_parameter, &context, &context_blob);
660  if (result != TPM_RC_SUCCESS) {
661    LOG(WARNING) << "Invalid context save response: " << GetErrorString(result);
662    return;
663  }
664  auto iter = session_handles_.find(saved_handle);
665  if (iter != session_handles_.end()) {
666    iter->second.is_loaded = false;
667    iter->second.context = context;
668  } else {
669    // Unknown handle? Not anymore.
670    LOG(WARNING) << "Context for unknown handle.";
671    HandleInfo new_handle_info;
672    new_handle_info.Init(saved_handle);
673    new_handle_info.is_loaded = false;
674    new_handle_info.context = context;
675    session_handles_[saved_handle] = new_handle_info;
676  }
677  // Use the original context data as the 'external' context data. If this gets
678  // virtualized, only the 'actual' context data will change.
679  external_context_to_actual_[context_blob] = context_blob;
680  actual_context_to_external_[context_blob] = context_blob;
681}
682
683std::string ResourceManager::ProcessFlushContext(
684    const std::string& command,
685    const MessageInfo& command_info) {
686  std::string buffer = command_info.parameter_data;
687  // There must be exactly one handle in the parameters section.
688  std::vector<TPM_HANDLE> handles = ExtractHandlesFromBuffer(1, &buffer);
689  if (handles.size() != 1) {
690    return CreateErrorResponse(MakeError(TPM_RC_SIZE, FROM_HERE));
691  }
692  TPM_HANDLE handle = handles[0];
693  TPM_HANDLE actual_handle = handle;
694  if (IsObjectHandle(handle)) {
695    auto iter = virtual_object_handles_.find(handle);
696    if (iter == virtual_object_handles_.end()) {
697      return CreateErrorResponse(MakeError(TPM_RC_HANDLE, FROM_HERE));
698    }
699    if (!iter->second.is_loaded) {
700      // The handle wasn't loaded so no need to bother the TPM.
701      CleanupFlushedHandle(handle);
702      return CreateErrorResponse(TPM_RC_SUCCESS);
703    }
704    actual_handle = iter->second.tpm_handle;
705  }
706  // Send a command with the original header but with |actual_handle| as the
707  // parameter.
708  std::string handle_blob;
709  Serialize_TPM_HANDLE(actual_handle, &handle_blob);
710  std::string updated_command =
711      command.substr(0, kMessageHeaderSize) + handle_blob;
712  // No need to loop and fix warnings, there are no actionable warnings on when
713  // flushing context.
714  std::string response = next_transceiver_->SendCommandAndWait(updated_command);
715  MessageInfo response_info;
716  TPM_RC result = ParseResponse(command_info, response, &response_info);
717  if (result != TPM_RC_SUCCESS) {
718    return CreateErrorResponse(result);
719  }
720  // Cleanup the handle locally even if the TPM did not recognize it.
721  if (response_info.code == TPM_RC_SUCCESS ||
722      response_info.code == TPM_RC_HANDLE) {
723    CleanupFlushedHandle(handle);
724  }
725  return response;
726}
727
728TPM_RC ResourceManager::ProcessInputHandle(const MessageInfo& command_info,
729                                           TPM_HANDLE virtual_handle,
730                                           TPM_HANDLE* actual_handle) {
731  // Only transient object handles are virtualized.
732  if (!IsObjectHandle(virtual_handle)) {
733    *actual_handle = virtual_handle;
734    return TPM_RC_SUCCESS;
735  }
736  auto handle_iter = virtual_object_handles_.find(virtual_handle);
737  if (handle_iter == virtual_object_handles_.end()) {
738    return MakeError(TPM_RC_HANDLE, FROM_HERE);
739  }
740  HandleInfo& handle_info = handle_iter->second;
741  if (!handle_info.is_loaded) {
742    TPM_RC result = LoadContext(command_info, &handle_info);
743    if (result != TPM_RC_SUCCESS) {
744      return result;
745    }
746    tpm_object_handles_[handle_info.tpm_handle] = virtual_handle;
747    VLOG(1) << "RELOAD_OBJECT: " << std::hex << virtual_handle;
748  }
749  VLOG(1) << "INPUT_HANDLE_REPLACE: " << std::hex << virtual_handle << " -> "
750          << std::hex << handle_info.tpm_handle;
751  *actual_handle = handle_info.tpm_handle;
752  return TPM_RC_SUCCESS;
753}
754
755TPM_HANDLE ResourceManager::ProcessOutputHandle(TPM_HANDLE handle) {
756  // Track, but do not virtualize, session handles.
757  if (IsSessionHandle(handle)) {
758    auto session_handle_iter = session_handles_.find(handle);
759    if (session_handle_iter == session_handles_.end()) {
760      HandleInfo new_handle_info;
761      new_handle_info.Init(handle);
762      session_handles_[handle] = new_handle_info;
763      VLOG(1) << "OUTPUT_HANDLE_NEW_SESSION: " << std::hex << handle;
764    }
765    return handle;
766  }
767  // Only transient object handles are virtualized.
768  if (!IsObjectHandle(handle)) {
769    return handle;
770  }
771  auto virtual_handle_iter = tpm_object_handles_.find(handle);
772  if (virtual_handle_iter == tpm_object_handles_.end()) {
773    TPM_HANDLE new_virtual_handle = CreateVirtualHandle();
774    HandleInfo new_handle_info;
775    new_handle_info.Init(handle);
776    virtual_object_handles_[new_virtual_handle] = new_handle_info;
777    tpm_object_handles_[handle] = new_virtual_handle;
778    VLOG(1) << "OUTPUT_HANDLE_NEW_VIRTUAL: " << std::hex << handle << " -> "
779            << std::hex << new_virtual_handle;
780    return new_virtual_handle;
781  }
782  VLOG(1) << "OUTPUT_HANDLE_REPLACE: " << std::hex << handle << " -> "
783          << std::hex << virtual_handle_iter->second;
784  return virtual_handle_iter->second;
785}
786
787std::string ResourceManager::ReplaceHandles(
788    const std::string& message,
789    const std::vector<TPM_HANDLE>& new_handles) {
790  std::string handles_blob;
791  for (auto handle : new_handles) {
792    CHECK_EQ(Serialize_TPM_HANDLE(handle, &handles_blob), TPM_RC_SUCCESS);
793  }
794  std::string mutable_message = message;
795  CHECK_GE(message.size(), kMessageHeaderSize + handles_blob.size());
796  return mutable_message.replace(kMessageHeaderSize, handles_blob.size(),
797                                 handles_blob);
798}
799
800TPM_RC ResourceManager::SaveContext(const MessageInfo& command_info,
801                                    HandleInfo* handle_info) {
802  CHECK(handle_info->is_loaded);
803  TPM_RC result = TPM_RC_SUCCESS;
804  int attempts = 0;
805  while (attempts++ < kMaxCommandAttempts) {
806    std::string tpm_handle_name;
807    Serialize_TPM_HANDLE(handle_info->tpm_handle, &tpm_handle_name);
808    result = factory_.GetTpm()->ContextSaveSync(handle_info->tpm_handle,
809                                                tpm_handle_name,
810                                                &handle_info->context, nullptr);
811    if (!FixWarnings(command_info, result)) {
812      break;
813    }
814  }
815  if (result != TPM_RC_SUCCESS) {
816    LOG(ERROR) << __func__
817               << ": Failed to load context: " << GetErrorString(result);
818    return result;
819  }
820  handle_info->is_loaded = false;
821  return result;
822}
823
824ResourceManager::HandleInfo::HandleInfo() : is_loaded(false), tpm_handle(0) {
825  memset(&context, 0, sizeof(TPMS_CONTEXT));
826}
827
828void ResourceManager::HandleInfo::Init(TPM_HANDLE handle) {
829  tpm_handle = handle;
830  is_loaded = true;
831  time_of_create = base::TimeTicks::Now();
832  time_of_last_use = base::TimeTicks::Now();
833}
834
835}  // namespace trunks
836