| // Copyright 2016 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 "authpolicy/policy/policy_encoder_helper.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include <base/bind.h> |
| #include <base/bind_helpers.h> |
| #include <base/callback.h> |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/run_loop.h> |
| #include <base/strings/string16.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/utf_string_conversions.h> |
| #include <base/system/sys_info.h> |
| #include <components/policy/core/common/policy_load_status.h> |
| #include <components/policy/core/common/registry_dict.h> |
| |
| #include "authpolicy/log_colors.h" |
| #include "authpolicy/policy/preg_parser.h" |
| #include "bindings/policy_common_definitions.pb.h" |
| #include "bindings/policy_constants.h" |
| |
| namespace em = enterprise_management; |
| |
| namespace policy { |
| |
| constexpr char kKeyUserDevice[] = "Software\\Policies\\Google\\ChromeOS"; |
| constexpr char kKeyExtensions[] = |
| "Software\\Policies\\Google\\Chrome\\3rdparty\\Extensions"; |
| constexpr char kKeyRecommended[] = "Recommended"; |
| constexpr char kKeyMandatoryExtension[] = "Policy"; |
| |
| bool LoadPRegFileIntoDict(const base::FilePath& preg_file, |
| const char* registry_key, |
| RegistryDict* dict) { |
| if (!base::PathExists(preg_file)) { |
| LOG(ERROR) << "PReg file '" << preg_file.value() << "' does not exist"; |
| return false; |
| } |
| |
| // Note: Don't use PolicyLoadStatusUmaReporter here, it leaks, see |
| // crbug.com/717888. Simply eat the status and report a less fine-grained |
| // ERROR_PARSE_PREG_FAILED error in authpolicy. It would be possible to get |
| // the load status into authpolicy, but that would require a lot of plumbing |
| // since this code usually runs in a sandboxed process. |
| PolicyLoadStatusSampler status; |
| const base::string16 registry_key_utf16 = base::ASCIIToUTF16(registry_key); |
| if (!preg_parser::ReadFile(preg_file, registry_key_utf16, dict, &status)) { |
| LOG(ERROR) << "Failed to parse preg file '" << preg_file.value() << "'"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool LoadPRegFilesIntoDict(const std::vector<base::FilePath>& preg_files, |
| const char* registry_key, |
| RegistryDict* policy_dict) { |
| for (const base::FilePath& preg_file : preg_files) { |
| if (!LoadPRegFileIntoDict(preg_file, registry_key, policy_dict)) |
| return false; |
| } |
| return true; |
| } |
| |
| bool GetAsBoolean(const base::Value* value, bool* bool_value) { |
| if (value->GetAsBoolean(bool_value)) |
| return true; |
| |
| // Boolean policies are represented as integer 0/1 in the registry. |
| int int_value = 0; |
| if (value->GetAsInteger(&int_value) && (int_value == 0 || int_value == 1)) { |
| *bool_value = int_value != 0; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool GetAsInteger(const base::Value* value, int* int_value) { |
| return value->GetAsInteger(int_value); |
| } |
| |
| bool GetAsString(const base::Value* value, std::string* string_value) { |
| return value->GetAsString(string_value); |
| } |
| |
| void PrintConversionError(const base::Value* value, |
| const char* target_type, |
| const char* policy_name, |
| const std::string* index_str) { |
| LOG(ERROR) << "Failed to convert value '" << *value << " of type '" |
| << base::Value::GetTypeName(value->type()) << "'" |
| << " to " << target_type << " for policy '" << policy_name << "'" |
| << (index_str ? " at index " + *index_str : ""); |
| } |
| |
| bool GetAsIntegerInRangeAndPrintError(const base::Value* value, |
| int range_min, |
| int range_max, |
| const char* policy_name, |
| int* int_value) { |
| *int_value = 0; |
| if (!GetAsInteger(value, int_value)) { |
| PrintConversionError(value, "integer", policy_name); |
| return false; |
| } |
| |
| if (*int_value < range_min || *int_value > range_max) { |
| *int_value = 0; |
| LOG(ERROR) << "Value of policy '" << policy_name << "' is " << value |
| << ", outside of expected range [" << range_min << "," |
| << range_max << "]"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| PolicyValueCallback GetValueFromDictCallback(const RegistryDict* policy_dict) { |
| return base::BindRepeating( |
| [](const RegistryDict* dict, const std::string& policy_name) { |
| return dict->GetValue(policy_name); |
| }, |
| policy_dict); |
| } |
| |
| void SetPolicyOptions(em::PolicyOptions* options, PolicyLevel level) { |
| DCHECK(options); |
| options->set_mode(level == POLICY_LEVEL_RECOMMENDED |
| ? em::PolicyOptions_PolicyMode_RECOMMENDED |
| : em::PolicyOptions_PolicyMode_MANDATORY); |
| } |
| |
| base::Optional<bool> EncodeBooleanPolicy(const char* policy_name, |
| PolicyValueCallback get_policy_value, |
| bool log_policy_value) { |
| const base::Value* value = get_policy_value.Run(policy_name); |
| |
| if (!value) |
| return base::nullopt; |
| |
| // Get actual value, doing type conversion if necessary. |
| bool bool_value; |
| if (!GetAsBoolean(value, &bool_value)) { |
| PrintConversionError(value, "boolean", policy_name); |
| return base::nullopt; |
| } |
| |
| LOG_IF(INFO, log_policy_value) |
| << authpolicy::kColorPolicy << " " << policy_name << " = " |
| << (bool_value ? "true" : "false") << authpolicy::kColorReset; |
| |
| return base::make_optional(bool_value); |
| } |
| |
| base::Optional<int> EncodeIntegerInRangePolicy( |
| const char* policy_name, |
| PolicyValueCallback get_policy_value, |
| int range_min, |
| int range_max, |
| bool log_policy_value) { |
| const base::Value* value = get_policy_value.Run(policy_name); |
| if (!value) |
| return base::nullopt; |
| |
| // Get actual value, doing type conversion if necessary. |
| int int_value; |
| if (!GetAsIntegerInRangeAndPrintError(value, range_min, range_max, |
| policy_name, &int_value)) { |
| return base::nullopt; |
| } |
| |
| LOG_IF(INFO, log_policy_value) |
| << authpolicy::kColorPolicy << " " << policy_name << " = " << int_value |
| << authpolicy::kColorReset; |
| |
| return base::make_optional(int_value); |
| } |
| |
| base::Optional<std::string> EncodeStringPolicy( |
| const char* policy_name, |
| PolicyValueCallback get_policy_value, |
| bool log_policy_value) { |
| // Try to get policy value from dict. |
| const base::Value* value = get_policy_value.Run(policy_name); |
| if (!value) |
| return base::nullopt; |
| |
| // Get actual value, doing type conversion if necessary. |
| std::string string_value; |
| if (!GetAsString(value, &string_value)) { |
| PrintConversionError(value, "string", policy_name); |
| return base::nullopt; |
| } |
| |
| LOG_IF(INFO, log_policy_value) |
| << authpolicy::kColorPolicy << " " << policy_name << " = " |
| << string_value << authpolicy::kColorReset; |
| |
| return base::make_optional(string_value); |
| } |
| |
| base::Optional<std::vector<std::string>> EncodeStringListPolicy( |
| const char* policy_name, |
| PolicyValueCallback get_policy_value, |
| bool log_policy_value) { |
| // Get and check all values. Do this in advance to prevent partial writes. |
| std::vector<std::string> string_values; |
| for (int index = 0; /* empty */; ++index) { |
| std::string index_str = base::NumberToString(index + 1); |
| const base::Value* value = get_policy_value.Run(index_str); |
| if (!value) |
| break; |
| |
| std::string string_value; |
| if (!GetAsString(value, &string_value)) { |
| PrintConversionError(value, "string", policy_name, &index_str); |
| return base::nullopt; |
| } |
| string_values.push_back(string_value); |
| } |
| |
| if (log_policy_value && LOG_IS_ON(INFO)) { |
| LOG(INFO) << authpolicy::kColorPolicy << " " << policy_name |
| << authpolicy::kColorReset; |
| for (const std::string& value : string_values) |
| LOG(INFO) << authpolicy::kColorPolicy << " " << value |
| << authpolicy::kColorReset; |
| } |
| |
| return base::make_optional(string_values); |
| } |
| |
| } // namespace policy |