| // Copyright 2018 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/resilient_policy_store.h" |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include <base/containers/adapters.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/sha1.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <policy/policy_util.h> |
| #include <policy/resilient_policy_util.h> |
| |
| #include "login_manager/login_metrics.h" |
| #include "login_manager/system_utils_impl.h" |
| |
| namespace login_manager { |
| |
| namespace { |
| |
| // The path of temporary file to be saved when policy cleanup is done. The path |
| // is extended with policy path hash as suffix. The file is stored on tmpfs, so |
| // it is intentionally lost on each shutdown/restart of the device. |
| const char kCleanupDoneFilePrefix[] = |
| "/run/session_manager/policy_cleanup_done-"; |
| |
| // Maximum number of valid policy files to be kept by policy cleanup. |
| const int kMaxPolicyFileCount = 3; |
| |
| // Returns the path to cleanup_done temporary file associated with |
| // |policy_path|. |
| base::FilePath GetCleanupDoneFilePath(const base::FilePath& policy_path) { |
| const std::string policy_path_hash = |
| base::SHA1HashString(policy_path.value()); |
| const std::string policy_path_hex = |
| base::HexEncode(policy_path_hash.c_str(), policy_path_hash.size()); |
| const std::string cleanup_done_path(kCleanupDoneFilePrefix + policy_path_hex); |
| return base::FilePath(cleanup_done_path); |
| } |
| |
| // Checks if the cleanup_done temporary file associated with |policy_path| |
| // exists. If not present, creates it and returns true. Otherwise returns false. |
| bool CreateCleanupDoneFile(const base::FilePath& policy_path) { |
| const base::FilePath cleanup_done_path = GetCleanupDoneFilePath(policy_path); |
| if (!base::PathExists(cleanup_done_path)) { |
| base::File file(cleanup_done_path, |
| base::File::FLAG_CREATE | base::File::FLAG_WRITE); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| ResilientPolicyStore::ResilientPolicyStore(const base::FilePath& policy_path, |
| LoginMetrics* metrics) |
| : PolicyStore(policy_path), metrics_(metrics) {} |
| |
| bool ResilientPolicyStore::LoadOrCreate() { |
| DCHECK(metrics_); |
| std::map<int, base::FilePath> sorted_policy_file_paths = |
| policy::GetSortedResilientPolicyFilePaths(policy_path_); |
| if (sorted_policy_file_paths.empty()) |
| return true; |
| |
| // Try to load the existent policy files one by one in reverse order of their |
| // index until we succeed. The files that fail to be parsed are deleted. |
| int number_of_invalid_files = 0; |
| bool policy_loaded = false; |
| for (const auto& map_pair : base::Reversed(sorted_policy_file_paths)) { |
| const base::FilePath& policy_path = map_pair.second; |
| if (LoadOrCreateFromPath(policy_path)) { |
| policy_loaded = true; |
| break; |
| } |
| number_of_invalid_files++; |
| base::DeleteFile(policy_path, false); |
| } |
| |
| if (number_of_invalid_files > 0) { |
| // If at least one policy file has been deleted, we need to delete the |
| // |kCleanupDoneFileName| to make sure the next persist doesn't overwrite |
| // the data in a good file saved in a previous session. |
| base::DeleteFile(GetCleanupDoneFilePath(policy_path_), false); |
| } |
| metrics_->SendNumberOfInvalidPolicyFiles(number_of_invalid_files); |
| |
| return policy_loaded; |
| } |
| |
| bool ResilientPolicyStore::Persist() { |
| std::map<int, base::FilePath> sorted_policy_file_paths = |
| policy::GetSortedResilientPolicyFilePaths(policy_path_); |
| int new_index = sorted_policy_file_paths.empty() |
| ? 0 |
| : sorted_policy_file_paths.rbegin()->first; |
| |
| // The policy file where the data will be persisted has to be the latest |
| // that exists on disk (i.e. the highest index). But if it is the first |
| // persist after boot, the policy has to be saved in a new file. In that |
| // case the highest index present is incremented to have the new file with |
| // higher index. We determine if it's the first persist after boot by the |
| // absense of cleanup temporary file. The index is never reset as it is not |
| // realistic to expect int overflow in non-devmode here. |
| if (CreateCleanupDoneFile(policy_path_)) { |
| CleanupPolicyFiles(sorted_policy_file_paths); |
| new_index++; |
| } |
| // To be on the safe side, persist only in file with index >= 1. |
| new_index = std::max(new_index, 1); |
| |
| return PersistToPath( |
| policy::GetResilientPolicyFilePathForIndex(policy_path_, new_index)); |
| } |
| |
| bool ResilientPolicyStore::Delete() { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void ResilientPolicyStore::CleanupPolicyFiles( |
| const std::map<int, base::FilePath>& sorted_policy_file_paths) { |
| DCHECK(metrics_); |
| int number_of_good_files = 0; |
| int number_of_invalid_files = 0; |
| // Allow one less file, since we need room for the new file to be persisted |
| // after cleanup. |
| const int max_allowed_files = kMaxPolicyFileCount - 1; |
| for (const auto& map_pair : base::Reversed(sorted_policy_file_paths)) { |
| const base::FilePath& policy_path = map_pair.second; |
| if (number_of_good_files >= max_allowed_files) { |
| base::DeleteFile(policy_path, false); |
| continue; |
| } |
| |
| std::string polstr; |
| enterprise_management::PolicyFetchResponse policy; |
| policy::LoadPolicyResult result = |
| policy::LoadPolicyFromPath(policy_path, &polstr, &policy); |
| switch (result) { |
| case policy::LoadPolicyResult::kSuccess: |
| number_of_good_files++; |
| break; |
| case policy::LoadPolicyResult::kFileNotFound: |
| break; |
| case policy::LoadPolicyResult::kFailedToReadFile: |
| case policy::LoadPolicyResult::kEmptyFile: |
| case policy::LoadPolicyResult::kInvalidPolicyData: |
| number_of_invalid_files++; |
| base::DeleteFile(policy_path, false); |
| break; |
| } |
| } |
| metrics_->SendNumberOfInvalidPolicyFiles(number_of_invalid_files); |
| } |
| |
| } // namespace login_manager |