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