| // Copyright 2013 The Chromium 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 "components/proxy_config/proxy_policy_handler.h" |
| |
| #include <stddef.h> |
| |
| #include "base/check.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/values.h" |
| #include "components/policy/core/browser/configuration_policy_handler.h" |
| #include "components/policy/core/browser/policy_error_map.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/core/common/proxy_settings_constants.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/prefs/pref_value_map.h" |
| #include "components/proxy_config/proxy_config_dictionary.h" |
| #include "components/proxy_config/proxy_config_pref_names.h" |
| #include "components/strings/grit/components_strings.h" |
| |
| namespace { |
| |
| using policy::kProxyPacMandatory; |
| using policy::PolicyErrorMap; |
| using policy::PolicyMap; |
| using policy::key::kProxyBypassList; |
| using policy::key::kProxyMode; |
| using policy::key::kProxyPacUrl; |
| using policy::key::kProxyServer; |
| using policy::key::kProxyServerMode; |
| using policy::key::kProxySettings; |
| |
| // This is used to check whether for a given ProxyMode value, the ProxyPacUrl, |
| // the ProxyBypassList and the ProxyServer policies are allowed to be specified. |
| // |error_message_id| is the message id of the localized error message to show |
| // when the policies are not specified as allowed. Each value of ProxyMode |
| // has a ProxyModeValidationEntry in the |kProxyModeValidationMap| below. |
| struct ProxyModeValidationEntry { |
| const char* mode_value; |
| bool pac_url_allowed; |
| bool pac_mandatory_allowed; |
| bool bypass_list_allowed; |
| bool server_allowed; |
| int error_message_id; |
| }; |
| |
| // List of entries determining which proxy policies can be specified, depending |
| // on the ProxyMode. |
| constexpr ProxyModeValidationEntry kProxyModeValidationMap[] = { |
| {ProxyPrefs::kDirectProxyModeName, false, false, false, false, |
| IDS_POLICY_PROXY_MODE_DISABLED_ERROR}, |
| {ProxyPrefs::kAutoDetectProxyModeName, false, false, false, false, |
| IDS_POLICY_PROXY_MODE_AUTO_DETECT_ERROR}, |
| {ProxyPrefs::kPacScriptProxyModeName, true, true, false, false, |
| IDS_POLICY_PROXY_MODE_PAC_URL_ERROR}, |
| {ProxyPrefs::kFixedServersProxyModeName, false, false, true, true, |
| IDS_POLICY_PROXY_MODE_FIXED_SERVERS_ERROR}, |
| {ProxyPrefs::kSystemProxyModeName, false, false, false, false, |
| IDS_POLICY_PROXY_MODE_SYSTEM_ERROR}, |
| }; |
| |
| // Cannot be constexpr because the values of the strings are defined in an |
| // automatically generated .cc file. |
| const char* const kDeprecatedProxyPolicies[] = { |
| kProxyMode, kProxyServerMode, kProxyServer, kProxyPacUrl, kProxyBypassList, |
| }; |
| |
| // Maps the separate deprecated policies for proxy settings into a single |
| // Dictionary policy. This allows to keep the logic of merging policies from |
| // different sources simple, as all separate proxy policies should be considered |
| // as a single whole during merging. Returns proxy_settings value. |
| base::Value RemapProxyPolicies(const PolicyMap& policies) { |
| // The highest (level, scope) pair for an existing proxy policy is determined |
| // first, and then only policies with those exact attributes are merged. |
| PolicyMap::Entry current_priority; // Defaults to the lowest priority. |
| policy::PolicySource inherited_source = |
| policy::POLICY_SOURCE_ENTERPRISE_DEFAULT; |
| base::Value proxy_settings(base::Value::Type::DICTIONARY); |
| for (auto* policy : kDeprecatedProxyPolicies) { |
| const PolicyMap::Entry* entry = policies.Get(policy); |
| if (!entry) |
| continue; |
| if (policies.EntryHasHigherPriority(*entry, current_priority)) { |
| proxy_settings = base::Value(base::Value::Type::DICTIONARY); |
| current_priority = entry->DeepCopy(); |
| if (entry->source > inherited_source) // Higher priority? |
| inherited_source = entry->source; |
| } |
| // If two priorities are the same. |
| if (!policies.EntryHasHigherPriority(*entry, current_priority) && |
| !policies.EntryHasHigherPriority(current_priority, *entry)) { |
| // |value_unsafe| is used due to multiple policy types being handled. |
| proxy_settings.SetKey(policy, entry->value_unsafe()->Clone()); |
| } |
| } |
| // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the |
| // new priority is higher. |
| const PolicyMap::Entry* existing = policies.Get(kProxySettings); |
| if (!proxy_settings.DictEmpty() && |
| (!existing || |
| policies.EntryHasHigherPriority(current_priority, *existing))) { |
| return proxy_settings; |
| } else if (existing && existing->value(base::Value::Type::DICT)) { |
| return existing->value(base::Value::Type::DICT)->Clone(); |
| } |
| return base::Value(); |
| } |
| |
| } // namespace |
| |
| namespace proxy_config { |
| |
| // The proxy policies have the peculiarity that they are loaded from individual |
| // policies, but the providers then expose them through a unified |
| // DictionaryValue. |
| |
| ProxyPolicyHandler::ProxyPolicyHandler() {} |
| |
| ProxyPolicyHandler::~ProxyPolicyHandler() {} |
| |
| bool ProxyPolicyHandler::CheckPolicySettings(const PolicyMap& policies, |
| PolicyErrorMap* errors) { |
| base::Value proxy_settings = RemapProxyPolicies(policies); |
| const base::Value* mode = GetProxyPolicyValue(&proxy_settings, kProxyMode); |
| const base::Value* server = |
| GetProxyPolicyValue(&proxy_settings, kProxyServer); |
| const base::Value* server_mode = |
| GetProxyPolicyValue(&proxy_settings, kProxyServerMode); |
| const base::Value* pac_url = |
| GetProxyPolicyValue(&proxy_settings, kProxyPacUrl); |
| const base::Value* pac_mandatory = |
| GetProxyPolicyValue(&proxy_settings, kProxyPacMandatory); |
| const base::Value* bypass_list = |
| GetProxyPolicyValue(&proxy_settings, kProxyBypassList); |
| |
| if ((server || pac_url || bypass_list) && !(mode || server_mode)) { |
| errors->AddError(kProxySettings, kProxyMode, |
| IDS_POLICY_NOT_SPECIFIED_ERROR); |
| return false; |
| } |
| |
| std::string mode_value; |
| if (!CheckProxyModeAndServerMode(&proxy_settings, errors, &mode_value)) |
| return false; |
| |
| // If neither ProxyMode nor ProxyServerMode are specified, mode_value will be |
| // empty and the proxy shouldn't be configured at all. |
| if (mode_value.empty()) |
| return true; |
| |
| bool is_valid_mode = false; |
| for (size_t i = 0; i != std::size(kProxyModeValidationMap); ++i) { |
| const ProxyModeValidationEntry& entry = kProxyModeValidationMap[i]; |
| if (entry.mode_value != mode_value) |
| continue; |
| |
| is_valid_mode = true; |
| |
| if (!entry.pac_url_allowed && pac_url) { |
| errors->AddError(kProxySettings, kProxyPacUrl, entry.error_message_id); |
| } |
| if (!entry.pac_mandatory_allowed && pac_mandatory) { |
| errors->AddError(kProxySettings, kProxyPacMandatory, |
| entry.error_message_id); |
| } |
| if (!entry.bypass_list_allowed && bypass_list) { |
| errors->AddError(kProxySettings, kProxyBypassList, |
| entry.error_message_id); |
| } |
| if (!entry.server_allowed && server) { |
| errors->AddError(kProxySettings, kProxyServer, entry.error_message_id); |
| } |
| |
| if ((!entry.pac_url_allowed && pac_url) || |
| (!entry.pac_mandatory_allowed && pac_mandatory) || |
| (!entry.bypass_list_allowed && bypass_list) || |
| (!entry.server_allowed && server)) { |
| return false; |
| } |
| } |
| |
| if (!is_valid_mode) { |
| errors->AddError(kProxySettings, mode ? kProxyMode : kProxyServerMode, |
| IDS_POLICY_OUT_OF_RANGE_ERROR, mode_value); |
| return false; |
| } |
| return true; |
| } |
| |
| void ProxyPolicyHandler::ApplyPolicySettings(const PolicyMap& policies, |
| PrefValueMap* prefs) { |
| base::Value proxy_settings = RemapProxyPolicies(policies); |
| const base::Value* mode = GetProxyPolicyValue(&proxy_settings, kProxyMode); |
| const base::Value* server = |
| GetProxyPolicyValue(&proxy_settings, kProxyServer); |
| const base::Value* server_mode = |
| GetProxyPolicyValue(&proxy_settings, kProxyServerMode); |
| const base::Value* pac_url = |
| GetProxyPolicyValue(&proxy_settings, kProxyPacUrl); |
| const base::Value* pac_mandatory = |
| GetProxyPolicyValue(&proxy_settings, kProxyPacMandatory); |
| const base::Value* bypass_list = |
| GetProxyPolicyValue(&proxy_settings, kProxyBypassList); |
| |
| ProxyPrefs::ProxyMode proxy_mode; |
| if (mode) { |
| CHECK(mode->is_string()); |
| CHECK(ProxyPrefs::StringToProxyMode(mode->GetString(), &proxy_mode)); |
| } else if (server_mode) { |
| switch (server_mode->GetInt()) { |
| case PROXY_SERVER_MODE: |
| proxy_mode = ProxyPrefs::MODE_DIRECT; |
| break; |
| case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: |
| proxy_mode = ProxyPrefs::MODE_AUTO_DETECT; |
| break; |
| case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: |
| proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS; |
| if (pac_url) |
| proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT; |
| break; |
| case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: |
| proxy_mode = ProxyPrefs::MODE_SYSTEM; |
| break; |
| default: |
| proxy_mode = ProxyPrefs::MODE_DIRECT; |
| NOTREACHED(); |
| } |
| } else { |
| return; |
| } |
| |
| switch (proxy_mode) { |
| case ProxyPrefs::MODE_DIRECT: |
| prefs->SetValue(proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreateDirect()); |
| break; |
| case ProxyPrefs::MODE_AUTO_DETECT: |
| prefs->SetValue(proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreateAutoDetect()); |
| break; |
| case ProxyPrefs::MODE_PAC_SCRIPT: { |
| if (pac_url && pac_url->is_string()) { |
| bool mandatory = |
| pac_mandatory && pac_mandatory->GetIfBool().value_or(false); |
| prefs->SetValue(proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreatePacScript( |
| pac_url->GetString(), mandatory)); |
| } else { |
| NOTREACHED(); |
| } |
| break; |
| } |
| case ProxyPrefs::MODE_FIXED_SERVERS: { |
| if (server->is_string()) { |
| prefs->SetValue( |
| proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreateFixedServers( |
| server->GetString(), bypass_list && bypass_list->is_string() |
| ? bypass_list->GetString() |
| : std::string())); |
| } |
| break; |
| } |
| case ProxyPrefs::MODE_SYSTEM: |
| prefs->SetValue(proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreateSystem()); |
| break; |
| case ProxyPrefs::kModeCount: |
| NOTREACHED(); |
| } |
| } |
| |
| const base::Value* ProxyPolicyHandler::GetProxyPolicyValue( |
| const base::Value* value, |
| const char* policy_name) { |
| const base::DictionaryValue* settings; |
| if (!value || !value->GetAsDictionary(&settings)) |
| return nullptr; |
| |
| const base::Value* policy_value = settings->FindPath(policy_name); |
| if (!policy_value || policy_value->is_none()) |
| return nullptr; |
| const std::string* tmp = policy_value->GetIfString(); |
| if (tmp && tmp->empty()) |
| return nullptr; |
| return policy_value; |
| } |
| |
| bool ProxyPolicyHandler::CheckProxyModeAndServerMode( |
| const base::Value* proxy_settings, |
| PolicyErrorMap* errors, |
| std::string* mode_value) { |
| const base::Value* mode = GetProxyPolicyValue(proxy_settings, kProxyMode); |
| const base::Value* server = GetProxyPolicyValue(proxy_settings, kProxyServer); |
| const base::Value* server_mode = |
| GetProxyPolicyValue(proxy_settings, kProxyServerMode); |
| const base::Value* pac_url = |
| GetProxyPolicyValue(proxy_settings, kProxyPacUrl); |
| |
| // If there's a server mode, convert it into a mode. |
| // When both are specified, the mode takes precedence. |
| if (mode) { |
| if (server_mode) { |
| errors->AddError(kProxySettings, kProxyServerMode, IDS_POLICY_OVERRIDDEN, |
| kProxyMode); |
| } |
| if (!mode->is_string()) { |
| errors->AddError(kProxySettings, kProxyMode, IDS_POLICY_TYPE_ERROR, |
| base::Value::GetTypeName(base::Value::Type::BOOLEAN)); |
| return false; |
| } |
| *mode_value = mode->GetString(); |
| |
| ProxyPrefs::ProxyMode proxy_mode; |
| if (!ProxyPrefs::StringToProxyMode(*mode_value, &proxy_mode)) { |
| errors->AddError(kProxySettings, kProxyMode, |
| IDS_POLICY_INVALID_PROXY_MODE_ERROR); |
| return false; |
| } |
| |
| if (proxy_mode == ProxyPrefs::MODE_PAC_SCRIPT && !pac_url) { |
| errors->AddError(kProxySettings, kProxyPacUrl, |
| IDS_POLICY_NOT_SPECIFIED_ERROR); |
| return false; |
| } |
| if (proxy_mode == ProxyPrefs::MODE_FIXED_SERVERS && !server) { |
| errors->AddError(kProxySettings, kProxyServer, |
| IDS_POLICY_NOT_SPECIFIED_ERROR); |
| return false; |
| } |
| } else if (server_mode) { |
| if (!server_mode->is_int()) { |
| errors->AddError(kProxySettings, kProxyServerMode, IDS_POLICY_TYPE_ERROR, |
| base::Value::GetTypeName(base::Value::Type::INTEGER)); |
| return false; |
| } |
| |
| switch (server_mode->GetInt()) { |
| case PROXY_SERVER_MODE: |
| *mode_value = ProxyPrefs::kDirectProxyModeName; |
| break; |
| case PROXY_AUTO_DETECT_PROXY_SERVER_MODE: |
| *mode_value = ProxyPrefs::kAutoDetectProxyModeName; |
| break; |
| case PROXY_MANUALLY_CONFIGURED_PROXY_SERVER_MODE: |
| if (server && pac_url) { |
| int message_id = IDS_POLICY_PROXY_BOTH_SPECIFIED_ERROR; |
| errors->AddError(kProxySettings, kProxyServer, message_id); |
| errors->AddError(kProxySettings, kProxyPacUrl, message_id); |
| return false; |
| } |
| if (!server && !pac_url) { |
| int message_id = IDS_POLICY_PROXY_NEITHER_SPECIFIED_ERROR; |
| errors->AddError(kProxySettings, kProxyServer, message_id); |
| errors->AddError(kProxySettings, kProxyPacUrl, message_id); |
| return false; |
| } |
| *mode_value = pac_url ? ProxyPrefs::kPacScriptProxyModeName |
| : ProxyPrefs::kFixedServersProxyModeName; |
| break; |
| case PROXY_USE_SYSTEM_PROXY_SERVER_MODE: |
| *mode_value = ProxyPrefs::kSystemProxyModeName; |
| break; |
| default: |
| errors->AddError(kProxySettings, kProxyServerMode, |
| IDS_POLICY_OUT_OF_RANGE_ERROR, |
| base::NumberToString(server_mode->GetInt())); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace proxy_config |