| // 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 "login_manager/policy_service.h" |
| |
| #include <stdint.h> |
| |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/callback.h> |
| #include <base/files/file_enumerator.h> |
| #include <base/location.h> |
| #include <base/logging.h> |
| #include <base/synchronization/waitable_event.h> |
| #include <brillo/message_loops/message_loop.h> |
| #include <chromeos/dbus/service_constants.h> |
| |
| #include "bindings/device_management_backend.pb.h" |
| #include "login_manager/blob_util.h" |
| #include "login_manager/dbus_util.h" |
| #include "login_manager/nss_util.h" |
| #include "login_manager/policy_key.h" |
| #include "login_manager/policy_store.h" |
| #include "login_manager/resilient_policy_store.h" |
| #include "login_manager/system_utils.h" |
| #include "login_manager/validator_utils.h" |
| |
| namespace em = enterprise_management; |
| |
| namespace login_manager { |
| |
| PolicyNamespace MakeChromePolicyNamespace() { |
| return std::make_pair(POLICY_DOMAIN_CHROME, std::string()); |
| } |
| |
| // Returns true if the domain, when part of a PolicyNamespace, expects a |
| // non-empty |component_id()|. |
| bool IsComponentDomain(PolicyDomain domain) { |
| switch (domain) { |
| case POLICY_DOMAIN_CHROME: |
| return false; |
| case POLICY_DOMAIN_EXTENSIONS: |
| case POLICY_DOMAIN_SIGNIN_EXTENSIONS: |
| return true; |
| } |
| NOTREACHED(); |
| return false; |
| } |
| |
| constexpr char PolicyService::kChromePolicyFileName[] = "policy"; |
| constexpr char PolicyService::kExtensionsPolicyFileNamePrefix[] = |
| "policy_extension_id_"; |
| constexpr char PolicyService::kSignInExtensionsPolicyFileNamePrefix[] = |
| "policy_signin_extension_id_"; |
| |
| PolicyService::PolicyService(const base::FilePath& policy_dir, |
| PolicyKey* policy_key, |
| LoginMetrics* metrics, |
| bool resilient_chrome_policy_store) |
| : metrics_(metrics), |
| policy_dir_(policy_dir), |
| policy_key_(policy_key), |
| resilient_chrome_policy_store_(resilient_chrome_policy_store), |
| delegate_(NULL), |
| weak_ptr_factory_(this) {} |
| |
| PolicyService::~PolicyService() = default; |
| |
| bool PolicyService::Store(const PolicyNamespace& ns, |
| const std::vector<uint8_t>& policy_blob, |
| int key_flags, |
| SignatureCheck signature_check, |
| const Completion& completion) { |
| em::PolicyFetchResponse policy; |
| if (!policy.ParseFromArray(policy_blob.data(), policy_blob.size()) || |
| !policy.has_policy_data()) { |
| completion.Run(CREATE_ERROR_AND_LOG(dbus_error::kSigDecodeFail, |
| "Unable to parse policy protobuf.")); |
| return false; |
| } |
| |
| return StorePolicy(ns, policy, key_flags, signature_check, completion); |
| } |
| |
| bool PolicyService::Retrieve(const PolicyNamespace& ns, |
| std::vector<uint8_t>* policy_blob) { |
| *policy_blob = SerializeAsBlob(GetOrCreateStore(ns)->Get()); |
| return true; |
| } |
| |
| bool PolicyService::Delete(const PolicyNamespace& ns, |
| SignatureCheck signature_check) { |
| // Don't delete Chrome user or device policy. |
| if (!IsComponentDomain(ns.first)) { |
| LOG(ERROR) << "Deletion only allowed for component policy."; |
| return false; |
| } |
| |
| // Don't delete signed policy. |
| if (signature_check != SignatureCheck::kDisabled) { |
| LOG(ERROR) << "Deletion of signed policy not allowed."; |
| return false; |
| } |
| |
| // Delete file on disk if it exists. |
| if (!GetOrCreateStore(ns)->Delete()) { |
| LOG(ERROR) << "Failed to delete store."; |
| return false; |
| } |
| |
| // Delete store. |
| policy_stores_.erase(ns); |
| |
| return true; |
| } |
| |
| std::vector<std::string> PolicyService::ListComponentIds(PolicyDomain domain) { |
| // Get all component IDs from policy files stored on disk. |
| std::vector<std::string> file_component_ids; |
| switch (domain) { |
| case POLICY_DOMAIN_CHROME: |
| // Does not support component IDs, early out. |
| return std::vector<std::string>(); |
| |
| case POLICY_DOMAIN_EXTENSIONS: |
| file_component_ids = FindComponentIds(kExtensionsPolicyFileNamePrefix, |
| &ValidateExtensionId); |
| break; |
| |
| case POLICY_DOMAIN_SIGNIN_EXTENSIONS: |
| file_component_ids = FindComponentIds( |
| kSignInExtensionsPolicyFileNamePrefix, &ValidateExtensionId); |
| break; |
| } |
| |
| // We might have missed some IDs from component policy that has not been |
| // written out yet, so check the stores as well. |
| std::set<std::string> component_ids(file_component_ids.begin(), |
| file_component_ids.end()); |
| for (const auto& kv : policy_stores_) { |
| const PolicyNamespace& ns = kv.first; |
| const std::string& component_id = ns.second; |
| const PolicyStore* store = kv.second.get(); |
| // Only count stores that actually have policy! |
| if (ns.first == domain && store->Get().has_policy_data()) |
| component_ids.insert(component_id); |
| } |
| |
| return std::vector<std::string>(component_ids.begin(), component_ids.end()); |
| } |
| |
| void PolicyService::PersistPolicy(const PolicyNamespace& ns, |
| const Completion& completion) { |
| const bool success = GetOrCreateStore(ns)->Persist(); |
| OnPolicyPersisted(completion, |
| success ? dbus_error::kNone : dbus_error::kSigEncodeFail); |
| } |
| |
| void PolicyService::PersistAllPolicy() { |
| for (const auto& kv : policy_stores_) |
| kv.second->Persist(); |
| } |
| |
| PolicyStore* PolicyService::GetOrCreateStore(const PolicyNamespace& ns) { |
| PolicyStoreMap::const_iterator iter = policy_stores_.find(ns); |
| if (iter != policy_stores_.end()) |
| return iter->second.get(); |
| |
| bool resilient = |
| (ns == MakeChromePolicyNamespace() && resilient_chrome_policy_store_); |
| std::unique_ptr<PolicyStore> store; |
| if (resilient) |
| store = std::make_unique<ResilientPolicyStore>(GetPolicyPath(ns), metrics_); |
| else |
| store = std::make_unique<PolicyStore>(GetPolicyPath(ns)); |
| |
| store->EnsureLoadedOrCreated(); |
| PolicyStore* policy_store_ptr = store.get(); |
| policy_stores_[ns] = std::move(store); |
| return policy_store_ptr; |
| } |
| |
| void PolicyService::SetStoreForTesting(const PolicyNamespace& ns, |
| std::unique_ptr<PolicyStore> store) { |
| policy_stores_[ns] = std::move(store); |
| } |
| |
| void PolicyService::PostPersistKeyTask() { |
| brillo::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&PolicyService::PersistKey, weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void PolicyService::PostPersistPolicyTask(const PolicyNamespace& ns, |
| const Completion& completion) { |
| brillo::MessageLoop::current()->PostTask( |
| FROM_HERE, base::Bind(&PolicyService::PersistPolicy, |
| weak_ptr_factory_.GetWeakPtr(), ns, completion)); |
| } |
| |
| bool PolicyService::StorePolicy(const PolicyNamespace& ns, |
| const em::PolicyFetchResponse& policy, |
| int key_flags, |
| SignatureCheck signature_check, |
| const Completion& completion) { |
| if (signature_check == SignatureCheck::kDisabled) { |
| GetOrCreateStore(ns)->Set(policy); |
| PostPersistPolicyTask(ns, completion); |
| return true; |
| } |
| |
| // Determine if the policy has pushed a new owner key and, if so, set it. |
| if (policy.has_new_public_key() && !key()->Equals(policy.new_public_key())) { |
| // The policy contains a new key, and it is different from |key_|. |
| std::vector<uint8_t> der = StringToBlob(policy.new_public_key()); |
| |
| bool installed = false; |
| if (key()->IsPopulated()) { |
| if (policy.has_new_public_key_signature() && (key_flags & KEY_ROTATE)) { |
| // Graceful key rotation. |
| LOG(INFO) << "Attempting policy key rotation."; |
| installed = |
| key()->Rotate(der, StringToBlob(policy.new_public_key_signature())); |
| } |
| } else if (key_flags & KEY_INSTALL_NEW) { |
| LOG(INFO) << "Attempting to install new policy key."; |
| installed = key()->PopulateFromBuffer(der); |
| } |
| if (!installed && (key_flags & KEY_CLOBBER)) { |
| LOG(INFO) << "Clobbering existing policy key."; |
| installed = key()->ClobberCompromisedKey(der); |
| } |
| |
| if (!installed) { |
| completion.Run(CREATE_ERROR_AND_LOG(dbus_error::kPubkeySetIllegal, |
| "Failed to install policy key!")); |
| return false; |
| } |
| |
| // If here, need to persist the key just loaded into memory to disk. |
| PostPersistKeyTask(); |
| } |
| |
| // Validate signature on policy and persist to disk. |
| if (!key()->Verify(StringToBlob(policy.policy_data()), |
| StringToBlob(policy.policy_data_signature()))) { |
| completion.Run(CREATE_ERROR_AND_LOG(dbus_error::kVerifyFail, |
| "Signature could not be verified.")); |
| return false; |
| } |
| |
| GetOrCreateStore(ns)->Set(policy); |
| PostPersistPolicyTask(ns, completion); |
| return true; |
| } |
| |
| void PolicyService::OnKeyPersisted(bool status) { |
| if (status) |
| LOG(INFO) << "Persisted policy key to disk."; |
| else |
| LOG(ERROR) << "Failed to persist policy key to disk."; |
| if (delegate_) |
| delegate_->OnKeyPersisted(status); |
| } |
| |
| void PolicyService::OnPolicyPersisted(const Completion& completion, |
| const std::string& dbus_error_code) { |
| brillo::ErrorPtr error; |
| if (dbus_error_code != dbus_error::kNone) { |
| constexpr char kMessage[] = "Failed to persist policy to disk."; |
| LOG(ERROR) << kMessage << ": " << dbus_error_code; |
| error = CreateError(dbus_error_code, kMessage); |
| } |
| |
| if (!completion.is_null()) |
| completion.Run(std::move(error)); |
| else |
| error.reset(); |
| |
| if (delegate_) |
| delegate_->OnPolicyPersisted(dbus_error_code == dbus_error::kNone); |
| } |
| |
| void PolicyService::PersistKey() { |
| OnKeyPersisted(key()->Persist()); |
| } |
| |
| base::FilePath PolicyService::GetPolicyPath(const PolicyNamespace& ns) { |
| // If the store has already been already created, return the store's path. |
| PolicyStoreMap::const_iterator iter = policy_stores_.find(ns); |
| if (iter != policy_stores_.end()) |
| return iter->second->policy_path(); |
| |
| const PolicyDomain& domain = ns.first; |
| const std::string& component_id = ns.second; |
| switch (domain) { |
| case POLICY_DOMAIN_CHROME: |
| return policy_dir_.AppendASCII(kChromePolicyFileName); |
| case POLICY_DOMAIN_EXTENSIONS: |
| // Double-check extension ID (should have already been checked before). |
| CHECK(ValidateExtensionId(component_id)); |
| return policy_dir_.AppendASCII(kExtensionsPolicyFileNamePrefix + |
| component_id); |
| case POLICY_DOMAIN_SIGNIN_EXTENSIONS: |
| // Double-check extension ID (should have already been checked before). |
| CHECK(ValidateExtensionId(component_id)); |
| return policy_dir_.AppendASCII(kSignInExtensionsPolicyFileNamePrefix + |
| component_id); |
| } |
| } |
| |
| std::vector<std::string> PolicyService::FindComponentIds( |
| const std::string& policy_filename_prefix, ComponentIdFilter filter) { |
| std::vector<std::string> component_ids; |
| base::FileEnumerator policy_files(policy_dir_, false /* recursive */, |
| base::FileEnumerator::FILES, |
| policy_filename_prefix + "*"); |
| base::FilePath policy_path; |
| while (!(policy_path = policy_files.Next()).empty()) { |
| const base::FilePath policy_filename = policy_path.BaseName(); |
| DCHECK_GE(policy_filename.value().size(), policy_filename_prefix.size()); |
| std::string component_id = |
| policy_filename.value().substr(policy_filename_prefix.size()); |
| if (filter(component_id)) |
| component_ids.push_back(std::move(component_id)); |
| } |
| return component_ids; |
| } |
| |
| } // namespace login_manager |