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