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, ¶meter_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