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