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