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