1 // Copyright (c) 2012 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 "chromeos/network/onc/onc_merger.h"
6
7 #include <set>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/values.h"
16 #include "chromeos/network/onc/onc_signature.h"
17 #include "chromeos/network/policy_util.h"
18 #include "components/onc/onc_constants.h"
19
20 namespace chromeos {
21 namespace onc {
22 namespace {
23
24 typedef std::unique_ptr<base::DictionaryValue> DictionaryPtr;
25
26 // Returns true if the field is the identifier of a configuration, i.e. the GUID
27 // of a network or a certificate.
IsIdentifierField(const OncValueSignature & value_signature,const std::string & field_name)28 bool IsIdentifierField(const OncValueSignature& value_signature,
29 const std::string& field_name) {
30 if (&value_signature == &kNetworkConfigurationSignature)
31 return field_name == ::onc::network_config::kGUID;
32 if (&value_signature == &kCertificateSignature)
33 return field_name == ::onc::certificate::kGUID;
34 return false;
35 }
36
37 // Identifier fields and other read-only fields (specifically Type) are
38 // handled specially during merging because they are always identical for the
39 // various setting sources.
IsReadOnlyField(const OncValueSignature & value_signature,const std::string & field_name)40 bool IsReadOnlyField(const OncValueSignature& value_signature,
41 const std::string& field_name) {
42 if (IsIdentifierField(value_signature, field_name))
43 return true;
44 if (&value_signature == &kNetworkConfigurationSignature)
45 return field_name == ::onc::network_config::kType;
46 return false;
47 }
48
49 // Inserts |true| at every field name in |result| that is recommended in
50 // |policy|.
MarkRecommendedFieldnames(const base::DictionaryValue & policy,base::DictionaryValue * result)51 void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
52 base::DictionaryValue* result) {
53 const base::ListValue* recommended_value = NULL;
54 if (!policy.GetListWithoutPathExpansion(::onc::kRecommended,
55 &recommended_value))
56 return;
57 for (base::ListValue::const_iterator it = recommended_value->begin();
58 it != recommended_value->end(); ++it) {
59 std::string entry;
60 if (it->GetAsString(&entry))
61 result->SetKey(entry, base::Value(true));
62 }
63 }
64
65 // Returns a dictionary which contains |true| at each path that is editable by
66 // the user. No other fields are set.
GetEditableFlags(const base::DictionaryValue & policy)67 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
68 DictionaryPtr result_editable(new base::DictionaryValue);
69 MarkRecommendedFieldnames(policy, result_editable.get());
70
71 // Recurse into nested dictionaries.
72 for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd();
73 it.Advance()) {
74 const base::DictionaryValue* child_policy = NULL;
75 if (it.key() == ::onc::kRecommended ||
76 !it.value().GetAsDictionary(&child_policy)) {
77 continue;
78 }
79
80 result_editable->SetWithoutPathExpansion(it.key(),
81 GetEditableFlags(*child_policy));
82 }
83 return result_editable;
84 }
85
86 // This is the base class for merging a list of DictionaryValues in
87 // parallel. See MergeDictionaries function.
88 class MergeListOfDictionaries {
89 public:
90 typedef std::vector<const base::DictionaryValue*> DictPtrs;
91
92 MergeListOfDictionaries() = default;
93
94 virtual ~MergeListOfDictionaries() = default;
95
96 // For each path in any of the dictionaries |dicts|, the function
97 // MergeListOfValues is called with the list of values that are located at
98 // that path in each of the dictionaries. This function returns a new
99 // dictionary containing all results of MergeListOfValues at the respective
100 // paths. The resulting dictionary doesn't contain empty dictionaries.
MergeDictionaries(const DictPtrs & dicts)101 DictionaryPtr MergeDictionaries(const DictPtrs &dicts) {
102 DictionaryPtr result(new base::DictionaryValue);
103 std::set<std::string> visited;
104 for (DictPtrs::const_iterator it_outer = dicts.begin();
105 it_outer != dicts.end(); ++it_outer) {
106 if (!*it_outer)
107 continue;
108
109 for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
110 field.Advance()) {
111 const std::string& key = field.key();
112 if (key == ::onc::kRecommended || !visited.insert(key).second)
113 continue;
114
115 std::unique_ptr<base::Value> merged_value;
116 if (field.value().is_dict()) {
117 DictPtrs nested_dicts;
118 for (DictPtrs::const_iterator it_inner = dicts.begin();
119 it_inner != dicts.end(); ++it_inner) {
120 const base::DictionaryValue* nested_dict = NULL;
121 if (*it_inner)
122 (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
123 nested_dicts.push_back(nested_dict);
124 }
125 DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts));
126 if (!merged_dict->empty())
127 merged_value = std::move(merged_dict);
128 } else {
129 std::vector<const base::Value*> values;
130 for (DictPtrs::const_iterator it_inner = dicts.begin();
131 it_inner != dicts.end(); ++it_inner) {
132 const base::Value* value = NULL;
133 if (*it_inner)
134 (*it_inner)->GetWithoutPathExpansion(key, &value);
135 values.push_back(value);
136 }
137 merged_value = MergeListOfValues(key, values);
138 }
139
140 if (merged_value)
141 result->SetWithoutPathExpansion(key, std::move(merged_value));
142 }
143 }
144 return result;
145 }
146
147 protected:
148 // This function is called by MergeDictionaries for each list of values that
149 // are located at the same path in each of the dictionaries. The order of the
150 // values is the same as of the given dictionaries |dicts|. If a dictionary
151 // doesn't contain a path then it's value is NULL.
152 virtual std::unique_ptr<base::Value> MergeListOfValues(
153 const std::string& key,
154 const std::vector<const base::Value*>& values) = 0;
155
MergeNestedDictionaries(const std::string & key,const DictPtrs & dicts)156 virtual DictionaryPtr MergeNestedDictionaries(const std::string& key,
157 const DictPtrs &dicts) {
158 return MergeDictionaries(dicts);
159 }
160
161 private:
162 DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
163 };
164
165 // This is the base class for merging policies and user settings.
166 class MergeSettingsAndPolicies : public MergeListOfDictionaries {
167 public:
168 struct ValueParams {
169 const base::Value* user_policy;
170 const base::Value* device_policy;
171 const base::Value* user_setting;
172 const base::Value* shared_setting;
173 const base::Value* active_setting;
174 bool user_editable;
175 bool device_editable;
176 };
177
178 MergeSettingsAndPolicies() = default;
179
180 // Merge the provided dictionaries. For each path in any of the dictionaries,
181 // MergeValues is called. Its results are collected in a new dictionary which
182 // is then returned. The resulting dictionary never contains empty
183 // dictionaries.
MergeDictionaries(const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)184 DictionaryPtr MergeDictionaries(
185 const base::DictionaryValue* user_policy,
186 const base::DictionaryValue* device_policy,
187 const base::DictionaryValue* user_settings,
188 const base::DictionaryValue* shared_settings,
189 const base::DictionaryValue* active_settings) {
190 hasUserPolicy_ = (user_policy != NULL);
191 hasDevicePolicy_ = (device_policy != NULL);
192
193 DictionaryPtr user_editable;
194 if (user_policy != NULL)
195 user_editable = GetEditableFlags(*user_policy);
196
197 DictionaryPtr device_editable;
198 if (device_policy != NULL)
199 device_editable = GetEditableFlags(*device_policy);
200
201 std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL);
202 dicts[kUserPolicyIndex] = user_policy;
203 dicts[kDevicePolicyIndex] = device_policy;
204 dicts[kUserSettingsIndex] = user_settings;
205 dicts[kSharedSettingsIndex] = shared_settings;
206 dicts[kActiveSettingsIndex] = active_settings;
207 dicts[kUserEditableIndex] = user_editable.get();
208 dicts[kDeviceEditableIndex] = device_editable.get();
209 return MergeListOfDictionaries::MergeDictionaries(dicts);
210 }
211
212 protected:
213 // This function is called by MergeDictionaries for each list of values that
214 // are located at the same path in each of the dictionaries. Implementations
215 // can use the Has*Policy functions.
216 virtual std::unique_ptr<base::Value> MergeValues(
217 const std::string& key,
218 const ValueParams& values) = 0;
219
220 // Whether a user policy was provided.
HasUserPolicy()221 bool HasUserPolicy() {
222 return hasUserPolicy_;
223 }
224
225 // Whether a device policy was provided.
HasDevicePolicy()226 bool HasDevicePolicy() {
227 return hasDevicePolicy_;
228 }
229
230 // MergeListOfDictionaries override.
MergeListOfValues(const std::string & key,const std::vector<const base::Value * > & values)231 std::unique_ptr<base::Value> MergeListOfValues(
232 const std::string& key,
233 const std::vector<const base::Value*>& values) override {
234 bool user_editable = !HasUserPolicy();
235 if (values[kUserEditableIndex])
236 values[kUserEditableIndex]->GetAsBoolean(&user_editable);
237
238 bool device_editable = !HasDevicePolicy();
239 if (values[kDeviceEditableIndex])
240 values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
241
242 ValueParams params;
243 params.user_policy = values[kUserPolicyIndex];
244 params.device_policy = values[kDevicePolicyIndex];
245 params.user_setting = values[kUserSettingsIndex];
246 params.shared_setting = values[kSharedSettingsIndex];
247 params.active_setting = values[kActiveSettingsIndex];
248 params.user_editable = user_editable;
249 params.device_editable = device_editable;
250 return MergeValues(key, params);
251 }
252
253 private:
254 enum {
255 kUserPolicyIndex,
256 kDevicePolicyIndex,
257 kUserSettingsIndex,
258 kSharedSettingsIndex,
259 kActiveSettingsIndex,
260 kUserEditableIndex,
261 kDeviceEditableIndex,
262 kLastIndex
263 };
264
265 bool hasUserPolicy_, hasDevicePolicy_;
266
267 DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies);
268 };
269
270 // Call MergeDictionaries to merge policies and settings to the effective
271 // values. This ignores the active settings of Shill. See the description of
272 // MergeSettingsAndPoliciesToEffective.
273 class MergeToEffective : public MergeSettingsAndPolicies {
274 public:
275 MergeToEffective() = default;
276
277 protected:
278 // Merges |values| to the effective value (Mandatory policy overwrites user
279 // settings overwrites shared settings overwrites recommended policy). |which|
280 // is set to the respective onc::kAugmentation* constant that indicates which
281 // source of settings is effective. Note that this function may return a NULL
282 // pointer and set |which| to ::onc::kAugmentationUserPolicy, which means that
283 // the
284 // user policy didn't set a value but also didn't recommend it, thus enforcing
285 // the empty value.
MergeValues(const std::string & key,const ValueParams & values,std::string * which)286 std::unique_ptr<base::Value> MergeValues(const std::string& key,
287 const ValueParams& values,
288 std::string* which) {
289 const base::Value* result = NULL;
290 which->clear();
291 if (!values.user_editable) {
292 result = values.user_policy;
293 *which = ::onc::kAugmentationUserPolicy;
294 } else if (!values.device_editable) {
295 result = values.device_policy;
296 *which = ::onc::kAugmentationDevicePolicy;
297 } else if (values.user_setting) {
298 result = values.user_setting;
299 *which = ::onc::kAugmentationUserSetting;
300 } else if (values.shared_setting) {
301 result = values.shared_setting;
302 *which = ::onc::kAugmentationSharedSetting;
303 } else if (values.user_policy) {
304 result = values.user_policy;
305 *which = ::onc::kAugmentationUserPolicy;
306 } else if (values.device_policy) {
307 result = values.device_policy;
308 *which = ::onc::kAugmentationDevicePolicy;
309 } else {
310 // Can be reached if the current field is recommended, but none of the
311 // dictionaries contained a value for it.
312 }
313 if (result)
314 return base::WrapUnique(result->DeepCopy());
315 return std::unique_ptr<base::Value>();
316 }
317
318 // MergeSettingsAndPolicies override.
MergeValues(const std::string & key,const ValueParams & values)319 std::unique_ptr<base::Value> MergeValues(const std::string& key,
320 const ValueParams& values) override {
321 std::string which;
322 return MergeValues(key, values, &which);
323 }
324
325 private:
326 DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
327 };
328
329 namespace {
330
331 // Returns true if all not-null values in |values| are equal to |value|.
AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams & values,const base::Value & value)332 bool AllPresentValuesEqual(const MergeSettingsAndPolicies::ValueParams& values,
333 const base::Value& value) {
334 if (values.user_policy && !value.Equals(values.user_policy))
335 return false;
336 if (values.device_policy && !value.Equals(values.device_policy))
337 return false;
338 if (values.user_setting && !value.Equals(values.user_setting))
339 return false;
340 if (values.shared_setting && !value.Equals(values.shared_setting))
341 return false;
342 if (values.active_setting && !value.Equals(values.active_setting))
343 return false;
344 return true;
345 }
346
347 } // namespace
348
349 // Call MergeDictionaries to merge policies and settings to an augmented
350 // dictionary which contains a dictionary for each value in the original
351 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
352 class MergeToAugmented : public MergeToEffective {
353 public:
354 MergeToAugmented() = default;
355
MergeDictionaries(const OncValueSignature & signature,const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)356 DictionaryPtr MergeDictionaries(
357 const OncValueSignature& signature,
358 const base::DictionaryValue* user_policy,
359 const base::DictionaryValue* device_policy,
360 const base::DictionaryValue* user_settings,
361 const base::DictionaryValue* shared_settings,
362 const base::DictionaryValue* active_settings) {
363 signature_ = &signature;
364 return MergeToEffective::MergeDictionaries(user_policy,
365 device_policy,
366 user_settings,
367 shared_settings,
368 active_settings);
369 }
370
371 protected:
372 // MergeSettingsAndPolicies override.
MergeValues(const std::string & key,const ValueParams & values)373 std::unique_ptr<base::Value> MergeValues(const std::string& key,
374 const ValueParams& values) override {
375 const OncFieldSignature* field = NULL;
376 if (signature_)
377 field = GetFieldSignature(*signature_, key);
378
379 if (!field) {
380 // This field is not part of the provided ONCSignature, thus it cannot be
381 // controlled by policy. Return the plain active value instead of an
382 // augmented dictionary.
383 if (values.active_setting)
384 return base::WrapUnique(values.active_setting->DeepCopy());
385 return nullptr;
386 }
387
388 // This field is part of the provided ONCSignature, thus it can be
389 // controlled by policy.
390 std::string which_effective;
391 std::unique_ptr<base::Value> effective_value =
392 MergeToEffective::MergeValues(key, values, &which_effective);
393
394 if (IsReadOnlyField(*signature_, key)) {
395 // Don't augment read-only fields (GUID and Type).
396 if (effective_value) {
397 // DCHECK that all provided fields are identical.
398 DCHECK(AllPresentValuesEqual(values, *effective_value))
399 << "Values do not match: " << key
400 << " Effective: " << *effective_value;
401 // Return the un-augmented field.
402 return effective_value;
403 }
404 if (values.active_setting) {
405 // Unmanaged networks have assigned (active) values.
406 return base::WrapUnique(values.active_setting->DeepCopy());
407 }
408 LOG(ERROR) << "Field has no effective value: " << key;
409 return nullptr;
410 }
411
412 std::unique_ptr<base::DictionaryValue> augmented_value(
413 new base::DictionaryValue);
414
415 if (values.active_setting) {
416 augmented_value->SetKey(::onc::kAugmentationActiveSetting,
417 values.active_setting->Clone());
418 }
419
420 if (!which_effective.empty()) {
421 augmented_value->SetKey(::onc::kAugmentationEffectiveSetting,
422 base::Value(which_effective));
423 }
424
425 // Prevent credentials from being forwarded in cleartext to UI.
426 // User/shared credentials are not stored separately, so they cannot
427 // leak here.
428 // User and Shared settings are already replaced with |kFakeCredential|.
429 bool is_credential = onc::FieldIsCredential(*signature_, key);
430 if (is_credential) {
431 // Set |kFakeCredential| to notify UI that credential is saved.
432 if (values.user_policy) {
433 augmented_value->SetKey(
434 ::onc::kAugmentationUserPolicy,
435 base::Value(chromeos::policy_util::kFakeCredential));
436 }
437 if (values.device_policy) {
438 augmented_value->SetKey(
439 ::onc::kAugmentationDevicePolicy,
440 base::Value(chromeos::policy_util::kFakeCredential));
441 }
442 if (values.active_setting) {
443 augmented_value->SetKey(
444 ::onc::kAugmentationActiveSetting,
445 base::Value(chromeos::policy_util::kFakeCredential));
446 }
447 } else {
448 if (values.user_policy) {
449 augmented_value->SetKey(::onc::kAugmentationUserPolicy,
450 values.user_policy->Clone());
451 }
452 if (values.device_policy) {
453 augmented_value->SetKey(::onc::kAugmentationDevicePolicy,
454 values.device_policy->Clone());
455 }
456 }
457 if (values.user_setting) {
458 augmented_value->SetKey(::onc::kAugmentationUserSetting,
459 values.user_setting->Clone());
460 }
461 if (values.shared_setting) {
462 augmented_value->SetKey(::onc::kAugmentationSharedSetting,
463 values.shared_setting->Clone());
464 }
465 if (HasUserPolicy() && values.user_editable) {
466 augmented_value->SetKey(::onc::kAugmentationUserEditable,
467 base::Value(true));
468 }
469 if (HasDevicePolicy() && values.device_editable) {
470 augmented_value->SetKey(::onc::kAugmentationDeviceEditable,
471 base::Value(true));
472 }
473 if (augmented_value->empty())
474 augmented_value.reset();
475 return std::move(augmented_value);
476 }
477
478 // MergeListOfDictionaries override.
MergeNestedDictionaries(const std::string & key,const DictPtrs & dicts)479 DictionaryPtr MergeNestedDictionaries(const std::string& key,
480 const DictPtrs& dicts) override {
481 DictionaryPtr result;
482 if (signature_) {
483 const OncValueSignature* enclosing_signature = signature_;
484 signature_ = NULL;
485
486 const OncFieldSignature* field =
487 GetFieldSignature(*enclosing_signature, key);
488 if (field)
489 signature_ = field->value_signature;
490 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
491
492 signature_ = enclosing_signature;
493 } else {
494 result = MergeToEffective::MergeNestedDictionaries(key, dicts);
495 }
496 return result;
497 }
498
499 private:
500 const OncValueSignature* signature_;
501 DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
502 };
503
504 } // namespace
505
MergeSettingsAndPoliciesToEffective(const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings)506 DictionaryPtr MergeSettingsAndPoliciesToEffective(
507 const base::DictionaryValue* user_policy,
508 const base::DictionaryValue* device_policy,
509 const base::DictionaryValue* user_settings,
510 const base::DictionaryValue* shared_settings) {
511 MergeToEffective merger;
512 return merger.MergeDictionaries(
513 user_policy, device_policy, user_settings, shared_settings, NULL);
514 }
515
MergeSettingsAndPoliciesToAugmented(const OncValueSignature & signature,const base::DictionaryValue * user_policy,const base::DictionaryValue * device_policy,const base::DictionaryValue * user_settings,const base::DictionaryValue * shared_settings,const base::DictionaryValue * active_settings)516 DictionaryPtr MergeSettingsAndPoliciesToAugmented(
517 const OncValueSignature& signature,
518 const base::DictionaryValue* user_policy,
519 const base::DictionaryValue* device_policy,
520 const base::DictionaryValue* user_settings,
521 const base::DictionaryValue* shared_settings,
522 const base::DictionaryValue* active_settings) {
523 MergeToAugmented merger;
524 return merger.MergeDictionaries(
525 signature, user_policy, device_policy, user_settings, shared_settings,
526 active_settings);
527 }
528
529 } // namespace onc
530 } // namespace chromeos
531