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 
29 namespace {
30 
31 const int kMaxCommandAttempts = 3;
32 const size_t kMinimumAuthorizationSize = 9;
33 const size_t kMessageHeaderSize = 10;
34 const trunks::TPM_HANDLE kMaxVirtualHandle =
35     (trunks::HR_TRANSIENT + trunks::HR_HANDLE_MASK);
36 
37 class ScopedBool {
38  public:
ScopedBool()39   ScopedBool() : target_(nullptr) {}
~ScopedBool()40   ~ScopedBool() {
41     if (target_) {
42       *target_ = false;
43     }
44   }
Enable(bool * target)45   void Enable(bool* target) {
46     target_ = target;
47     *target_ = true;
48   }
49  private:
50   bool* target_;
51 };
52 
53 }  // namespace
54 
55 namespace trunks {
56 
ResourceManager(const TrunksFactory & factory,CommandTransceiver * next_transceiver)57 ResourceManager::ResourceManager(const TrunksFactory& factory,
58                                  CommandTransceiver* next_transceiver)
59     : factory_(factory),
60       next_transceiver_(next_transceiver) {}
61 
~ResourceManager()62 ResourceManager::~ResourceManager() {}
63 
Initialize()64 void 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 
SendCommand(const std::string & command,const ResponseCallback & callback)105 void ResourceManager::SendCommand(
106     const std::string& command,
107     const ResponseCallback& callback) {
108   callback.Run(SendCommandAndWait(command));
109 }
110 
SendCommandAndWait(const std::string & command)111 std::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 
ChooseSessionToEvict(const std::vector<TPM_HANDLE> & sessions_to_retain,TPM_HANDLE * session_to_evict)203 bool 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 
CleanupFlushedHandle(TPM_HANDLE flushed_handle)232 void 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 
CreateVirtualHandle()261 TPM_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 
EnsureSessionIsLoaded(const MessageInfo & command_info,TPM_HANDLE session_handle)274 TPM_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 
EvictObjects(const MessageInfo & command_info)296 void 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 
EvictSession(const MessageInfo & command_info)322 void 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 
ExtractHandlesFromBuffer(size_t number_of_handles,std::string * buffer)335 std::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 
FixContextGap(const MessageInfo & command_info)348 void 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 
FixWarnings(const MessageInfo & command_info,TPM_RC result)393 bool 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 
FlushSession(const MessageInfo & command_info)432 void 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 
GetActualContextFromExternalContext(const std::string & external_context)447 std::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 
IsObjectHandle(TPM_HANDLE handle) const456 bool ResourceManager::IsObjectHandle(TPM_HANDLE handle) const {
457   return ((handle & HR_RANGE_MASK) == HR_TRANSIENT);
458 }
459 
IsSessionHandle(TPM_HANDLE handle) const460 bool 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 
LoadContext(const MessageInfo & command_info,HandleInfo * handle_info)465 TPM_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 
MakeError(TPM_RC tpm_error,const::tracked_objects::Location & location)487 TPM_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 
ParseCommand(const std::string & command,MessageInfo * command_info)494 TPM_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 
ParseResponse(const MessageInfo & command_info,const std::string & response,MessageInfo * response_info)579 TPM_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 
ProcessExternalContextSave(const MessageInfo & command_info,const MessageInfo & response_info)652 void 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 
ProcessFlushContext(const std::string & command,const MessageInfo & command_info)696 std::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 
ProcessInputHandle(const MessageInfo & command_info,TPM_HANDLE virtual_handle,TPM_HANDLE * actual_handle)741 TPM_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 
ProcessOutputHandle(TPM_HANDLE handle)768 TPM_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 
ReplaceHandles(const std::string & message,const std::vector<TPM_HANDLE> & new_handles)800 std::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 
SaveContext(const MessageInfo & command_info,HandleInfo * handle_info)813 TPM_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 
HandleInfo()839 ResourceManager::HandleInfo::HandleInfo()
840     : is_loaded(false),
841       tpm_handle(0) {
842   memset(&context, 0, sizeof(TPMS_CONTEXT));
843 }
844 
Init(TPM_HANDLE handle)845 void 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