| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/chromeos/arc/arc_policy_bridge.h" | |
| 6 | |
| 7 #include <memory> | |
| 8 #include <string> | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/json/json_reader.h" | |
| 12 #include "base/json/json_string_value_serializer.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/memory/ptr_util.h" | |
| 15 #include "base/values.h" | |
| 16 #include "chrome/browser/chromeos/profiles/profile_helper.h" | |
| 17 #include "chrome/browser/policy/profile_policy_connector.h" | |
| 18 #include "chrome/browser/policy/profile_policy_connector_factory.h" | |
| 19 #include "chromeos/network/onc/onc_utils.h" | |
| 20 #include "components/arc/arc_bridge_service.h" | |
| 21 #include "components/onc/onc_constants.h" | |
| 22 #include "components/policy/core/common/policy_map.h" | |
| 23 #include "components/policy/core/common/policy_namespace.h" | |
| 24 #include "components/policy/policy_constants.h" | |
| 25 #include "components/user_manager/user.h" | |
| 26 #include "mojo/public/cpp/bindings/string.h" | |
| 27 | |
| 28 namespace arc { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 const char kArcGlobalAppRestrictions[] = "globalAppRestrictions"; | |
| 33 const char kArcCaCerts[] = "caCerts"; | |
| 34 | |
| 35 // invert_bool_value: If the Chrome policy and the ARC policy with boolean value | |
| 36 // have opposite semantics, set this to true so the bool is inverted before | |
| 37 // being added. Otherwise, set it to false. | |
| 38 void MapBoolToBool(const std::string& arc_policy_name, | |
| 39 const std::string& policy_name, | |
| 40 const policy::PolicyMap& policy_map, | |
| 41 bool invert_bool_value, | |
| 42 base::DictionaryValue* filtered_policies) { | |
| 43 const base::Value* const policy_value = policy_map.GetValue(policy_name); | |
| 44 if (!policy_value) | |
| 45 return; | |
| 46 if (!policy_value->IsType(base::Value::TYPE_BOOLEAN)) { | |
| 47 LOG(ERROR) << "Policy " << policy_name << " is not a boolean."; | |
| 48 return; | |
| 49 } | |
| 50 bool bool_value; | |
| 51 policy_value->GetAsBoolean(&bool_value); | |
| 52 filtered_policies->SetBoolean(arc_policy_name, | |
| 53 bool_value != invert_bool_value); | |
| 54 } | |
| 55 | |
| 56 // int_true: value of Chrome OS policy for which arc policy is set to true. | |
| 57 // It is set to false for all other values. | |
| 58 void MapIntToBool(const std::string& arc_policy_name, | |
| 59 const std::string& policy_name, | |
| 60 const policy::PolicyMap& policy_map, | |
| 61 int int_true, | |
| 62 base::DictionaryValue* filtered_policies) { | |
| 63 const base::Value* const policy_value = policy_map.GetValue(policy_name); | |
| 64 if (!policy_value) | |
| 65 return; | |
| 66 if (!policy_value->IsType(base::Value::TYPE_INTEGER)) { | |
| 67 LOG(ERROR) << "Policy " << policy_name << " is not an integer."; | |
| 68 return; | |
| 69 } | |
| 70 int int_value; | |
| 71 policy_value->GetAsInteger(&int_value); | |
| 72 filtered_policies->SetBoolean(arc_policy_name, int_value == int_true); | |
| 73 } | |
| 74 | |
| 75 void AddGlobalAppRestriction(const std::string& arc_app_restriction_name, | |
| 76 const std::string& policy_name, | |
| 77 const policy::PolicyMap& policy_map, | |
| 78 base::DictionaryValue* filtered_policies) { | |
| 79 const base::Value* const policy_value = policy_map.GetValue(policy_name); | |
| 80 if (policy_value) { | |
| 81 base::DictionaryValue* global_app_restrictions = nullptr; | |
| 82 if (!filtered_policies->GetDictionary(kArcGlobalAppRestrictions, | |
| 83 &global_app_restrictions)) { | |
| 84 global_app_restrictions = new base::DictionaryValue(); | |
| 85 filtered_policies->Set(kArcGlobalAppRestrictions, | |
| 86 global_app_restrictions); | |
| 87 } | |
| 88 global_app_restrictions->SetWithoutPathExpansion( | |
| 89 arc_app_restriction_name, policy_value->CreateDeepCopy()); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 void AddOncCaCertsToPolicies(const policy::PolicyMap& policy_map, | |
| 94 base::DictionaryValue* filtered_policies) { | |
| 95 const base::Value* const policy_value = | |
| 96 policy_map.GetValue(policy::key::kArcCertificatesSyncMode); | |
| 97 int32_t mode = ArcCertsSyncMode::SYNC_DISABLED; | |
| 98 | |
| 99 // Old certs should be uninstalled if the sync is disabled or policy is not | |
| 100 // set. | |
| 101 if (!policy_value || !policy_value->GetAsInteger(&mode) || | |
| 102 mode != ArcCertsSyncMode::COPY_CA_CERTS) { | |
| 103 return; | |
| 104 } | |
| 105 | |
| 106 // Importing CA certificates from device policy is not allowed. | |
| 107 // Import only from user policy. | |
| 108 const base::Value* onc_policy_value = | |
| 109 policy_map.GetValue(policy::key::kOpenNetworkConfiguration); | |
| 110 if (!onc_policy_value) { | |
| 111 VLOG(1) << "onc policy is not set."; | |
| 112 return; | |
| 113 } | |
| 114 std::string onc_blob; | |
| 115 if (!onc_policy_value->GetAsString(&onc_blob)) { | |
| 116 LOG(ERROR) << "Value of onc policy has invalid format."; | |
| 117 return; | |
| 118 } | |
| 119 | |
| 120 base::ListValue certificates; | |
| 121 { | |
| 122 base::ListValue unused_network_configs; | |
| 123 base::DictionaryValue unused_global_network_config; | |
| 124 if (!chromeos::onc::ParseAndValidateOncForImport( | |
| 125 onc_blob, onc::ONCSource::ONC_SOURCE_USER_POLICY, | |
| 126 "" /* no passphrase */, &unused_network_configs, | |
| 127 &unused_global_network_config, &certificates)) { | |
| 128 LOG(ERROR) << "Value of onc policy has invalid format =" << onc_blob; | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 std::unique_ptr<base::ListValue> ca_certs( | |
| 133 base::MakeUnique<base::ListValue>()); | |
| 134 for (const auto& entry : certificates) { | |
| 135 const base::DictionaryValue* certificate = nullptr; | |
| 136 if (!entry->GetAsDictionary(&certificate)) { | |
| 137 DLOG(FATAL) << "Value of a certificate entry is not a dictionary " | |
| 138 << "value."; | |
| 139 continue; | |
| 140 } | |
| 141 | |
| 142 std::string cert_type; | |
| 143 certificate->GetStringWithoutPathExpansion(::onc::certificate::kType, | |
| 144 &cert_type); | |
| 145 if (cert_type != ::onc::certificate::kAuthority) | |
| 146 continue; | |
| 147 | |
| 148 const base::ListValue* trust_list = nullptr; | |
| 149 if (!certificate->GetListWithoutPathExpansion( | |
| 150 ::onc::certificate::kTrustBits, &trust_list)) { | |
| 151 continue; | |
| 152 } | |
| 153 | |
| 154 bool web_trust_flag = false; | |
| 155 for (const auto& list_val : *trust_list) { | |
| 156 std::string trust_type; | |
| 157 if (!list_val->GetAsString(&trust_type)) | |
| 158 NOTREACHED(); | |
| 159 | |
| 160 if (trust_type == ::onc::certificate::kWeb) { | |
| 161 // "Web" implies that the certificate is to be trusted for SSL | |
| 162 // identification. | |
| 163 web_trust_flag = true; | |
| 164 break; | |
| 165 } | |
| 166 } | |
| 167 if (!web_trust_flag) | |
| 168 continue; | |
| 169 | |
| 170 std::string x509_data; | |
| 171 if (!certificate->GetStringWithoutPathExpansion(::onc::certificate::kX509, | |
| 172 &x509_data)) { | |
| 173 continue; | |
| 174 } | |
| 175 | |
| 176 base::DictionaryValue data; | |
| 177 data.SetString("X509", x509_data); | |
| 178 ca_certs->Append(data.CreateDeepCopy()); | |
| 179 } | |
| 180 filtered_policies->Set(kArcCaCerts, std::move(ca_certs)); | |
| 181 } | |
| 182 | |
| 183 std::string GetFilteredJSONPolicies(const policy::PolicyMap& policy_map) { | |
| 184 base::DictionaryValue filtered_policies; | |
| 185 // Parse ArcPolicy as JSON string before adding other policies to the | |
| 186 // dictionary. | |
| 187 const base::Value* const app_policy_value = | |
| 188 policy_map.GetValue(policy::key::kArcPolicy); | |
| 189 if (app_policy_value) { | |
| 190 std::string app_policy_string; | |
| 191 app_policy_value->GetAsString(&app_policy_string); | |
| 192 std::unique_ptr<base::DictionaryValue> app_policy_dict = | |
| 193 base::DictionaryValue::From(base::JSONReader::Read(app_policy_string)); | |
| 194 if (app_policy_dict) { | |
| 195 // Need a deep copy of all values here instead of doing a swap, because | |
| 196 // JSONReader::Read constructs a dictionary whose StringValues are | |
| 197 // JSONStringValues which are based on StringPiece instead of string. | |
| 198 filtered_policies.MergeDictionary(app_policy_dict.get()); | |
| 199 } else { | |
| 200 LOG(ERROR) << "Value of ArcPolicy has invalid format: " | |
| 201 << app_policy_string; | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 // Keep them sorted by the ARC policy names. | |
| 206 MapBoolToBool("cameraDisabled", policy::key::kVideoCaptureAllowed, policy_map, | |
| 207 true, &filtered_policies); | |
| 208 MapBoolToBool("debuggingFeaturesDisabled", | |
| 209 policy::key::kDeveloperToolsDisabled, policy_map, false, | |
| 210 &filtered_policies); | |
| 211 MapBoolToBool("screenCaptureDisabled", policy::key::kDisableScreenshots, | |
| 212 policy_map, false, &filtered_policies); | |
| 213 MapIntToBool("shareLocationDisabled", policy::key::kDefaultGeolocationSetting, | |
| 214 policy_map, 2 /*BlockGeolocation*/, &filtered_policies); | |
| 215 MapBoolToBool("unmuteMicrophoneDisabled", policy::key::kAudioCaptureAllowed, | |
| 216 policy_map, true, &filtered_policies); | |
| 217 MapBoolToBool("mountPhysicalMediaDisabled", | |
| 218 policy::key::kExternalStorageDisabled, policy_map, false, | |
| 219 &filtered_policies); | |
| 220 | |
| 221 // Add global app restrictions. | |
| 222 AddGlobalAppRestriction("com.android.browser:URLBlacklist", | |
| 223 policy::key::kURLBlacklist, policy_map, | |
| 224 &filtered_policies); | |
| 225 AddGlobalAppRestriction("com.android.browser:URLWhitelist", | |
| 226 policy::key::kURLWhitelist, policy_map, | |
| 227 &filtered_policies); | |
| 228 | |
| 229 // Add CA certificates. | |
| 230 AddOncCaCertsToPolicies(policy_map, &filtered_policies); | |
| 231 | |
| 232 std::string policy_json; | |
| 233 JSONStringValueSerializer serializer(&policy_json); | |
| 234 serializer.Serialize(filtered_policies); | |
| 235 return policy_json; | |
| 236 } | |
| 237 | |
| 238 } // namespace | |
| 239 | |
| 240 ArcPolicyBridge::ArcPolicyBridge(ArcBridgeService* bridge_service) | |
| 241 : ArcService(bridge_service), binding_(this) { | |
| 242 VLOG(2) << "ArcPolicyBridge::ArcPolicyBridge"; | |
| 243 arc_bridge_service()->policy()->AddObserver(this); | |
| 244 } | |
| 245 | |
| 246 ArcPolicyBridge::ArcPolicyBridge(ArcBridgeService* bridge_service, | |
| 247 policy::PolicyService* policy_service) | |
| 248 : ArcService(bridge_service), | |
| 249 binding_(this), | |
| 250 policy_service_(policy_service) { | |
| 251 VLOG(2) << "ArcPolicyBridge::ArcPolicyBridge(bridge_service, policy_service)"; | |
| 252 arc_bridge_service()->policy()->AddObserver(this); | |
| 253 } | |
| 254 | |
| 255 ArcPolicyBridge::~ArcPolicyBridge() { | |
| 256 VLOG(2) << "ArcPolicyBridge::~ArcPolicyBridge"; | |
| 257 arc_bridge_service()->policy()->RemoveObserver(this); | |
| 258 } | |
| 259 | |
| 260 void ArcPolicyBridge::OverrideIsManagedForTesting(bool is_managed) { | |
| 261 is_managed_ = is_managed; | |
| 262 } | |
| 263 | |
| 264 void ArcPolicyBridge::OnInstanceReady() { | |
| 265 VLOG(1) << "ArcPolicyBridge::OnPolicyInstanceReady"; | |
| 266 if (policy_service_ == nullptr) { | |
| 267 InitializePolicyService(); | |
| 268 } | |
| 269 policy_service_->AddObserver(policy::POLICY_DOMAIN_CHROME, this); | |
| 270 | |
| 271 mojom::PolicyInstance* const policy_instance = | |
| 272 arc_bridge_service()->policy()->GetInstanceForMethod("Init"); | |
| 273 DCHECK(policy_instance); | |
| 274 policy_instance->Init(binding_.CreateInterfacePtrAndBind()); | |
| 275 } | |
| 276 | |
| 277 void ArcPolicyBridge::OnInstanceClosed() { | |
| 278 VLOG(1) << "ArcPolicyBridge::OnPolicyInstanceClosed"; | |
| 279 policy_service_->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this); | |
| 280 policy_service_ = nullptr; | |
| 281 } | |
| 282 | |
| 283 void ArcPolicyBridge::GetPolicies(const GetPoliciesCallback& callback) { | |
| 284 VLOG(1) << "ArcPolicyBridge::GetPolicies"; | |
| 285 if (!is_managed_) { | |
| 286 callback.Run(mojo::String("")); | |
| 287 return; | |
| 288 } | |
| 289 const policy::PolicyNamespace policy_namespace(policy::POLICY_DOMAIN_CHROME, | |
| 290 std::string()); | |
| 291 const policy::PolicyMap& policy_map = | |
| 292 policy_service_->GetPolicies(policy_namespace); | |
| 293 const std::string json_policies = GetFilteredJSONPolicies(policy_map); | |
| 294 callback.Run(mojo::String(json_policies)); | |
| 295 } | |
| 296 | |
| 297 void ArcPolicyBridge::OnPolicyUpdated(const policy::PolicyNamespace& ns, | |
| 298 const policy::PolicyMap& previous, | |
| 299 const policy::PolicyMap& current) { | |
| 300 VLOG(1) << "ArcPolicyBridge::OnPolicyUpdated"; | |
| 301 auto* instance = | |
| 302 arc_bridge_service()->policy()->GetInstanceForMethod("OnPolicyUpdated"); | |
| 303 if (!instance) | |
| 304 return; | |
| 305 instance->OnPolicyUpdated(); | |
| 306 } | |
| 307 | |
| 308 void ArcPolicyBridge::InitializePolicyService() { | |
| 309 const user_manager::User* const primary_user = | |
| 310 user_manager::UserManager::Get()->GetPrimaryUser(); | |
| 311 Profile* const profile = | |
| 312 chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user); | |
| 313 auto* profile_policy_connector = | |
| 314 policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile); | |
| 315 policy_service_ = profile_policy_connector->policy_service(); | |
| 316 is_managed_ = profile_policy_connector->IsManaged(); | |
| 317 } | |
| 318 | |
| 319 } // namespace arc | |
| OLD | NEW |