| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chaps/object_pool_impl.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <base/logging.h> |
| #include <base/synchronization/lock.h> |
| #include <base/synchronization/waitable_event.h> |
| |
| #include "chaps/chaps.h" |
| #include "chaps/chaps_factory.h" |
| #include "chaps/chaps_utility.h" |
| #include "chaps/handle_generator.h" |
| #include "chaps/object.h" |
| #include "chaps/object_importer.h" |
| #include "chaps/object_store.h" |
| #include "chaps/proto_bindings/attributes.pb.h" |
| |
| using base::AutoLock; |
| using base::AutoUnlock; |
| using chromeos::SecureBlob; |
| using std::map; |
| using std::string; |
| using std::shared_ptr; |
| using std::vector; |
| |
| namespace chaps { |
| |
| ObjectPoolImpl::ObjectPoolImpl(ChapsFactory* factory, |
| HandleGenerator* handle_generator, |
| ObjectStore* store, |
| ObjectImporter* importer) |
| : factory_(factory), |
| handle_generator_(handle_generator), |
| store_(store), |
| importer_(importer), |
| is_private_loaded_(false), |
| private_loaded_event_(true, false), // Manual reset, not signaled. |
| finish_import_required_(false) {} |
| |
| ObjectPoolImpl::~ObjectPoolImpl() {} |
| |
| bool ObjectPoolImpl::Init() { |
| AutoLock lock(lock_); |
| if (store_.get()) { |
| if (!LoadPublicObjects()) |
| return false; |
| // Import legacy objects. The existence of the 'imported' blob indicates |
| // that legacy objects have already been imported. The contents of this blob |
| // are ignored. |
| AutoUnlock unlock(lock_); |
| string imported_blob; |
| if (importer_.get() && !GetInternalBlob(kImportedTracker, &imported_blob)) { |
| finish_import_required_ = importer_->ImportObjects(this); |
| if (!SetInternalBlob(kImportedTracker, imported_blob)) { |
| LOG(WARNING) << "Failed to set the import tracker."; |
| } |
| } |
| } else { |
| // There are no objects to load. |
| is_private_loaded_ = true; |
| private_loaded_event_.Signal(); |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::GetInternalBlob(int blob_id, string* blob) { |
| AutoLock lock(lock_); |
| if (store_.get()) |
| return store_->GetInternalBlob(blob_id, blob); |
| return false; |
| } |
| |
| bool ObjectPoolImpl::SetInternalBlob(int blob_id, const string& blob) { |
| AutoLock lock(lock_); |
| if (store_.get()) |
| return store_->SetInternalBlob(blob_id, blob); |
| return false; |
| } |
| |
| bool ObjectPoolImpl::SetEncryptionKey(const SecureBlob& key) { |
| AutoLock lock(lock_); |
| if (key.empty()) |
| LOG(WARNING) << "WARNING: Private object services will not be available."; |
| if (store_.get() && !key.empty()) { |
| if (!store_->SetEncryptionKey(key)) |
| return false; |
| // Once we have the encryption key we can load private objects. |
| if (!LoadPrivateObjects()) |
| LOG(WARNING) << "Failed to load private objects."; |
| if (finish_import_required_) { |
| CHECK(importer_.get()); |
| // Unlock because FinishImportAsync inserts objects into this pool. |
| AutoUnlock unlock(lock_); |
| if (!importer_->FinishImportAsync(this)) |
| LOG(WARNING) << "Failed to finish importing objects."; |
| } |
| } |
| // Signal any callers waiting for private objects that they're ready. |
| is_private_loaded_ = true; |
| private_loaded_event_.Signal(); |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Insert(Object* object) { |
| // If it's a private object we need to wait until private objects have been |
| // loaded. |
| if (object->IsPrivate() && !is_private_loaded_) { |
| AutoLock lock(lock_); |
| WaitForPrivateObjects(); |
| } |
| return Import(object); |
| } |
| |
| bool ObjectPoolImpl::Import(Object* object) { |
| AutoLock lock(lock_); |
| if (objects_.find(object) != objects_.end()) |
| return false; |
| if (store_.get()) { |
| ObjectBlob serialized; |
| if (!Serialize(object, &serialized)) |
| return false; |
| // Parsing the serialized blob will normalize the object attribute values. |
| // e.g. If the caller specified 32-bits for a CK_ULONG on a 64-bit system, |
| // the value will be resized correctly. |
| if (!Parse(serialized, object)) |
| return false; |
| int store_id; |
| if (!store_->InsertObjectBlob(serialized, &store_id)) |
| return false; |
| object->set_store_id(store_id); |
| } |
| object->set_handle(handle_generator_->CreateHandle()); |
| objects_.insert(object); |
| handle_object_map_[object->handle()] = shared_ptr<const Object>(object); |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Delete(const Object* object) { |
| AutoLock lock(lock_); |
| if (objects_.find(object) == objects_.end()) |
| return false; |
| if (store_.get()) { |
| // If it's a private object we need to wait until private objects have been |
| // loaded. |
| if (object->IsPrivate() && !is_private_loaded_) |
| WaitForPrivateObjects(); |
| if (!store_->DeleteObjectBlob(object->store_id())) |
| return false; |
| } |
| handle_object_map_.erase(object->handle()); |
| objects_.erase(object); |
| return true; |
| } |
| |
| bool ObjectPoolImpl::DeleteAll() { |
| AutoLock lock(lock_); |
| objects_.clear(); |
| handle_object_map_.clear(); |
| if (store_.get()) |
| return store_->DeleteAllObjectBlobs(); |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Find(const Object* search_template, |
| vector<const Object*>* matching_objects) { |
| AutoLock lock(lock_); |
| // If we're looking for private objects we need to wait until private objects |
| // have been loaded. |
| if (((search_template->IsAttributePresent(CKA_PRIVATE) && |
| search_template->IsPrivate()) || |
| (search_template->IsAttributePresent(CKA_CLASS) && |
| search_template->GetObjectClass() == CKO_PRIVATE_KEY)) && |
| !is_private_loaded_) |
| WaitForPrivateObjects(); |
| for (ObjectSet::iterator it = objects_.begin(); it != objects_.end(); ++it) { |
| if (Matches(search_template, *it)) |
| matching_objects->push_back(*it); |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::FindByHandle(int handle, const Object** object) { |
| AutoLock lock(lock_); |
| CHECK(object); |
| HandleObjectMap::iterator it = handle_object_map_.find(handle); |
| if (it == handle_object_map_.end()) |
| return false; |
| *object = it->second.get(); |
| return true; |
| } |
| |
| Object* ObjectPoolImpl::GetModifiableObject(const Object* object) { |
| return const_cast<Object*>(object); |
| } |
| |
| bool ObjectPoolImpl::Flush(const Object* object) { |
| AutoLock lock(lock_); |
| if (objects_.find(object) == objects_.end()) |
| return false; |
| if (store_.get()) { |
| ObjectBlob serialized; |
| if (!Serialize(object, &serialized)) |
| return false; |
| // If it's a private object we need to wait until private objects have been |
| // loaded. |
| if (object->IsPrivate() && !is_private_loaded_) |
| WaitForPrivateObjects(); |
| if (!store_->UpdateObjectBlob(object->store_id(), serialized)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Matches(const Object* object_template, |
| const Object* object) { |
| const AttributeMap* attributes = object_template->GetAttributeMap(); |
| AttributeMap::const_iterator it; |
| for (it = attributes->begin(); it != attributes->end(); ++it) { |
| if (!object->IsAttributePresent(it->first)) |
| return false; |
| if (it->second != object->GetAttributeString(it->first)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Parse(const ObjectBlob& object_blob, Object* object) { |
| AttributeList attribute_list; |
| if (!attribute_list.ParseFromString(object_blob.blob)) { |
| LOG(ERROR) << "Failed to parse proto-buffer."; |
| return false; |
| } |
| for (int i = 0; i < attribute_list.attribute_size(); ++i) { |
| const Attribute& attribute = attribute_list.attribute(i); |
| if (!attribute.has_value()) { |
| LOG(WARNING) << "No value found for attribute: " << attribute.type(); |
| continue; |
| } |
| object->SetAttributeString(attribute.type(), attribute.value()); |
| // Correct the length of integral attributes since they may have been |
| // serialized with a different sizeof(CK_ULONG). |
| if (IsIntegralAttribute(attribute.type()) && |
| attribute.value().length() != sizeof(CK_ULONG)) { |
| int int_value = object->GetAttributeInt(attribute.type(), 0); |
| object->SetAttributeInt(attribute.type(), int_value); |
| } |
| if (attribute.type() == CKA_PRIVATE && |
| object->IsPrivate() != object_blob.is_private) { |
| // Assume this object has been tampered with. |
| LOG(ERROR) << "Privacy attribute mismatch."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::Serialize(const Object* object, ObjectBlob* serialized) { |
| const AttributeMap* attribute_map = object->GetAttributeMap(); |
| AttributeMap::const_iterator it; |
| AttributeList attribute_list; |
| for (it = attribute_map->begin(); it != attribute_map->end(); ++it) { |
| Attribute* next = attribute_list.add_attribute(); |
| next->set_type(it->first); |
| next->set_length(it->second.length()); |
| next->set_value(it->second); |
| } |
| if (!attribute_list.SerializeToString(&serialized->blob)) { |
| LOG(ERROR) << "Failed to serialize object."; |
| return false; |
| } |
| serialized->is_private = object->IsPrivate(); |
| return true; |
| } |
| |
| bool ObjectPoolImpl::LoadBlobs(const map<int, ObjectBlob>& object_blobs) { |
| map<int, ObjectBlob>::const_iterator it; |
| for (it = object_blobs.begin(); it != object_blobs.end(); ++it) { |
| shared_ptr<Object> object(factory_->CreateObject()); |
| // An object that is not parsable will be ignored. |
| if (Parse(it->second, object.get())) { |
| object->set_handle(handle_generator_->CreateHandle()); |
| object->set_store_id(it->first); |
| objects_.insert(object.get()); |
| handle_object_map_[object->handle()] = object; |
| } else { |
| LOG(WARNING) << "Object not parsable: " << it->first; |
| } |
| } |
| return true; |
| } |
| |
| bool ObjectPoolImpl::LoadPublicObjects() { |
| CHECK(store_.get()); |
| map<int, ObjectBlob> object_blobs; |
| if (!store_->LoadPublicObjectBlobs(&object_blobs)) |
| return false; |
| return LoadBlobs(object_blobs); |
| } |
| |
| bool ObjectPoolImpl::LoadPrivateObjects() { |
| CHECK(store_.get()); |
| map<int, ObjectBlob> object_blobs; |
| if (!store_->LoadPrivateObjectBlobs(&object_blobs)) |
| return false; |
| return LoadBlobs(object_blobs); |
| } |
| |
| void ObjectPoolImpl::WaitForPrivateObjects() { |
| AutoUnlock unlock(lock_); |
| LOG(INFO) << "Waiting for private objects to be loaded."; |
| private_loaded_event_.Wait(); |
| LOG(INFO) << "Done waiting for private objects."; |
| } |
| |
| } // namespace chaps |