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