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