| // Copyright 2017 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/preg_policy_writer.h" |
| |
| #include <limits> |
| |
| #include <base/files/file_util.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string16.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include "authpolicy/policy/policy_encoder_helper.h" |
| #include "authpolicy/policy/preg_parser.h" |
| |
| namespace { |
| |
| const char kActionTriggerDelVals[] = "**delvals"; |
| |
| // Constants for PReg file delimiters. |
| const base::char16 kDelimBracketOpen = L'['; |
| const base::char16 kDelimBracketClose = L']'; |
| const base::char16 kDelimSemicolon = L';'; |
| const base::char16 kDelimNull = L'\0'; |
| |
| // Registry data type constants. Values have to match REG_* constants on |
| // Windows. Does not check data types or whether a policy can be recommended or |
| // not. This is checked when GPO is converted to a policy proto. |
| const uint32_t kRegSz = 1; |
| const uint32_t kRegDwordLittleEndian = 4; |
| const uint32_t kRegMultiSz = 7; |
| |
| const uint32_t kMaxUInt32 = std::numeric_limits<uint32_t>::max(); |
| |
| } // namespace |
| |
| namespace policy { |
| |
| PRegPolicyWriter::PRegPolicyWriter(const std::string& mandatory_key, |
| const std::string& recommended_key) |
| : PRegPolicyWriter() { |
| SetMandatoryKey(mandatory_key); |
| SetRecommendedKey(recommended_key); |
| } |
| |
| PRegPolicyWriter::PRegPolicyWriter() { |
| buffer_.append( |
| preg_parser::kPRegFileHeader, |
| preg_parser::kPRegFileHeader + base::size(preg_parser::kPRegFileHeader)); |
| } |
| |
| PRegPolicyWriter::~PRegPolicyWriter() { |
| DCHECK(!entry_started_); |
| } |
| |
| void PRegPolicyWriter::SetMandatoryKey(const std::string& mandatory_key) { |
| mandatory_key_ = mandatory_key; |
| } |
| |
| void PRegPolicyWriter::SetRecommendedKey(const std::string& recommended_key) { |
| recommended_key_ = recommended_key; |
| } |
| |
| void PRegPolicyWriter::SetKeysForUserDevicePolicy() { |
| SetMandatoryKey(policy::kKeyUserDevice); |
| SetRecommendedKey( |
| base::StringPrintf("%s\\%s", policy::kKeyUserDevice, kKeyRecommended)); |
| } |
| |
| void PRegPolicyWriter::SetKeysForExtensionPolicy( |
| const std::string& extension_id) { |
| std::string base_key = base::StringPrintf("%s\\%s\\", policy::kKeyExtensions, |
| extension_id.c_str()); |
| SetMandatoryKey(base_key + kKeyMandatoryExtension); |
| SetRecommendedKey(base_key + kKeyRecommended); |
| } |
| |
| void PRegPolicyWriter::AppendBoolean(const char* policy_name, |
| bool value, |
| PolicyLevel level) { |
| DCHECK(!recommended_key_.empty() && !mandatory_key_.empty()); |
| |
| StartEntry(GetKey(level), policy_name, kRegDwordLittleEndian, |
| sizeof(uint32_t)); |
| AppendUnsignedInt(value ? 1 : 0); |
| EndEntry(); |
| } |
| |
| void PRegPolicyWriter::AppendInteger(const char* policy_name, |
| uint32_t value, |
| PolicyLevel level) { |
| DCHECK(!recommended_key_.empty() && !mandatory_key_.empty()); |
| |
| StartEntry(GetKey(level), policy_name, kRegDwordLittleEndian, |
| sizeof(uint32_t)); |
| AppendUnsignedInt(value); |
| EndEntry(); |
| } |
| |
| void PRegPolicyWriter::AppendString(const char* policy_name, |
| const std::string& value, |
| PolicyLevel level) { |
| DCHECK(!recommended_key_.empty() && !mandatory_key_.empty()); |
| |
| // size * 2 + 2 because of char16 and the 0-terminator. |
| CHECK(value.size() <= (kMaxUInt32 - 2) / 2); |
| StartEntry(GetKey(level), policy_name, kRegSz, |
| static_cast<uint32_t>(value.size()) * 2 + 2); |
| AppendNullTerminatedString(value); |
| EndEntry(); |
| } |
| |
| void PRegPolicyWriter::AppendMultiString(const char* policy_name, |
| const std::vector<std::string>& values, |
| PolicyLevel level) { |
| DCHECK(!recommended_key_.empty() && !mandatory_key_.empty()); |
| |
| uint32_t total_utf16_size = 0; |
| for (const std::string& value : values) { |
| CHECK(value.size() <= (kMaxUInt32 - 2) / 2); |
| // size * 2 + 2 because of char16 and the 0-terminator. |
| uint32_t value_utf16_size = value.size() * 2 + 2; |
| CHECK(value_utf16_size <= kMaxUInt32 - total_utf16_size); |
| total_utf16_size += value_utf16_size; |
| } |
| // After the last 0-terminated string comes an extra 0-terminator: |
| CHECK(total_utf16_size <= kMaxUInt32 - 2); |
| total_utf16_size += 2; |
| |
| StartEntry(GetKey(level), policy_name, kRegMultiSz, total_utf16_size); |
| for (const std::string& value : values) { |
| AppendNullTerminatedString(value); |
| } |
| AppendChar16(kDelimNull); |
| EndEntry(); |
| } |
| |
| void PRegPolicyWriter::AppendStringList(const char* policy_name, |
| const std::vector<std::string>& values, |
| PolicyLevel level) { |
| DCHECK(!recommended_key_.empty() && !mandatory_key_.empty()); |
| |
| // Add entry to wipe previous values. |
| std::string key = GetKey(level) + "\\" + policy_name; |
| StartEntry(key, kActionTriggerDelVals, kRegSz, 2); |
| AppendNullTerminatedString(""); |
| EndEntry(); |
| |
| // Add an entry for each value. |
| CHECK(values.size() <= kMaxUInt32 - 1); |
| for (int n = 0; n < static_cast<int>(values.size()); ++n) { |
| CHECK(values[n].size() <= (kMaxUInt32 - 2) / 2); |
| StartEntry(key, base::NumberToString(n + 1), kRegSz, |
| static_cast<uint32_t>(values[n].size()) * 2 + 2); |
| AppendNullTerminatedString(values[n]); |
| EndEntry(); |
| } |
| } |
| |
| bool PRegPolicyWriter::WriteToFile(const base::FilePath& path) { |
| int size = static_cast<int>(buffer_.size()); |
| return base::WriteFile(path, buffer_.data(), size) == size; |
| } |
| |
| void PRegPolicyWriter::StartEntry(const std::string& key_name, |
| const std::string& value_name, |
| uint32_t data_type, |
| uint32_t data_size) { |
| DCHECK(!entry_started_); |
| entry_started_ = true; |
| |
| AppendChar16(kDelimBracketOpen); |
| |
| AppendNullTerminatedString(key_name); |
| AppendChar16(kDelimSemicolon); |
| |
| AppendNullTerminatedString(value_name); |
| AppendChar16(kDelimSemicolon); |
| |
| AppendUnsignedInt(data_type); |
| AppendChar16(kDelimSemicolon); |
| |
| AppendUnsignedInt(data_size); |
| AppendChar16(kDelimSemicolon); |
| } |
| |
| void PRegPolicyWriter::EndEntry() { |
| AppendChar16(kDelimBracketClose); |
| |
| DCHECK(entry_started_); |
| entry_started_ = false; |
| } |
| |
| void PRegPolicyWriter::AppendNullTerminatedString(const std::string& str) { |
| for (char ch : str) |
| AppendChar16(static_cast<base::char16>(ch)); |
| AppendChar16(kDelimNull); |
| } |
| |
| void PRegPolicyWriter::AppendUnsignedInt(uint32_t value) { |
| AppendChar16(value & 0xffff); |
| AppendChar16(value >> 16); |
| } |
| |
| void PRegPolicyWriter::AppendChar16(base::char16 ch) { |
| buffer_.append(1, ch & 0xff); |
| buffer_.append(1, ch >> 8); |
| } |
| |
| const std::string& PRegPolicyWriter::GetKey(PolicyLevel level) { |
| return level == POLICY_LEVEL_RECOMMENDED ? recommended_key_ : mandatory_key_; |
| } |
| |
| PRegUserDevicePolicyWriter::PRegUserDevicePolicyWriter() { |
| SetKeysForUserDevicePolicy(); |
| } |
| |
| PRegExtensionPolicyWriter::PRegExtensionPolicyWriter( |
| const std::string& extension_id) { |
| SetKeysForExtensionPolicy(extension_id); |
| } |
| |
| } // namespace policy |