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: 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 50 private: 51 bool* target_; 52 }; 53 54 } // namespace 55 56 namespace trunks { 57 58 ResourceManager::ResourceManager(const TrunksFactory& factory, 59 CommandTransceiver* next_transceiver) 60 : factory_(factory), next_transceiver_(next_transceiver) {} 61 62 ResourceManager::~ResourceManager() {} 63 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 98 void ResourceManager::SendCommand(const std::string& command, 99 const ResponseCallback& callback) { 100 callback.Run(SendCommandAndWait(command)); 101 } 102 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 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 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 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 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 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 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 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 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 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 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 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 445 bool ResourceManager::IsObjectHandle(TPM_HANDLE handle) const { 446 return ((handle & HR_RANGE_MASK) == HR_TRANSIENT); 447 } 448 449 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 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 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 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 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, ¶meter_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 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 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 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 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 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 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 824 ResourceManager::HandleInfo::HandleInfo() : is_loaded(false), tpm_handle(0) { 825 memset(&context, 0, sizeof(TPMS_CONTEXT)); 826 } 827 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