Home | History | Annotate | Download | only in network
      1 // Copyright (c) 2013 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/managed_network_configuration_handler.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/guid.h"
     12 #include "base/json/json_writer.h"
     13 #include "base/location.h"
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/memory/scoped_ptr.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/values.h"
     20 #include "chromeos/dbus/dbus_method_call_status.h"
     21 #include "chromeos/dbus/dbus_thread_manager.h"
     22 #include "chromeos/dbus/shill_manager_client.h"
     23 #include "chromeos/dbus/shill_profile_client.h"
     24 #include "chromeos/dbus/shill_service_client.h"
     25 #include "chromeos/network/network_configuration_handler.h"
     26 #include "chromeos/network/network_event_log.h"
     27 #include "chromeos/network/network_handler_callbacks.h"
     28 #include "chromeos/network/network_profile.h"
     29 #include "chromeos/network/network_profile_handler.h"
     30 #include "chromeos/network/network_state.h"
     31 #include "chromeos/network/network_state_handler.h"
     32 #include "chromeos/network/network_ui_data.h"
     33 #include "chromeos/network/onc/onc_constants.h"
     34 #include "chromeos/network/onc/onc_merger.h"
     35 #include "chromeos/network/onc/onc_normalizer.h"
     36 #include "chromeos/network/onc/onc_signature.h"
     37 #include "chromeos/network/onc/onc_translator.h"
     38 #include "chromeos/network/onc/onc_utils.h"
     39 #include "chromeos/network/onc/onc_validator.h"
     40 #include "dbus/object_path.h"
     41 #include "third_party/cros_system_api/dbus/service_constants.h"
     42 
     43 namespace chromeos {
     44 
     45 namespace {
     46 
     47 // These are error strings used for error callbacks. None of these error
     48 // messages are user-facing: they should only appear in logs.
     49 const char kInvalidUserSettingsMessage[] = "User settings are invalid.";
     50 const char kInvalidUserSettings[] = "Error.InvalidUserSettings";
     51 const char kNetworkAlreadyConfiguredMessage[] =
     52     "Network is already configured.";
     53 const char kNetworkAlreadyConfigured[] = "Error.NetworkAlreadyConfigured";
     54 const char kPoliciesNotInitializedMessage[] = "Policies not initialized.";
     55 const char kPoliciesNotInitialized[] = "Error.PoliciesNotInitialized";
     56 const char kProfileNotInitializedMessage[] = "Profile not initialized.";
     57 const char kProfileNotInitialized[] = "Error.ProflieNotInitialized";
     58 const char kSetOnUnconfiguredNetworkMessage[] =
     59     "Unable to modify properties of an unconfigured network.";
     60 const char kSetOnUnconfiguredNetwork[] = "Error.SetCalledOnUnconfiguredNetwork";
     61 const char kUnknownProfilePathMessage[] = "Profile path is unknown.";
     62 const char kUnknownProfilePath[] = "Error.UnknownProfilePath";
     63 const char kUnknownServicePathMessage[] = "Service path is unknown.";
     64 const char kUnknownServicePath[] = "Error.UnknownServicePath";
     65 
     66 // This fake credential contains a random postfix which is extremly unlikely to
     67 // be used by any user.
     68 const char kFakeCredential[] = "FAKE_CREDENTIAL_VPaJDV9x";
     69 
     70 std::string ToDebugString(onc::ONCSource source,
     71                           const std::string& userhash) {
     72   return source == onc::ONC_SOURCE_USER_POLICY ?
     73       ("user policy of " + userhash) : "device policy";
     74 }
     75 
     76 void RunErrorCallback(const std::string& service_path,
     77                       const std::string& error_name,
     78                       const std::string& error_message,
     79                       const network_handler::ErrorCallback& error_callback) {
     80   NET_LOG_ERROR(error_name, error_message);
     81   error_callback.Run(
     82       error_name,
     83       make_scoped_ptr(
     84           network_handler::CreateErrorData(service_path,
     85                                            error_name,
     86                                            error_message)));
     87 }
     88 
     89 // Sets the UIData property in |shill_dictionary| to the serialization of
     90 // |ui_data|.
     91 void SetUIData(const NetworkUIData& ui_data,
     92                base::DictionaryValue* shill_dictionary) {
     93   base::DictionaryValue ui_data_dict;
     94   ui_data.FillDictionary(&ui_data_dict);
     95   std::string ui_data_blob;
     96   base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
     97   shill_dictionary->SetStringWithoutPathExpansion(flimflam::kUIDataProperty,
     98                                                   ui_data_blob);
     99 }
    100 
    101 // A dummy callback to ignore the result of Shill calls.
    102 void IgnoreString(const std::string& str) {
    103 }
    104 
    105 void LogErrorWithDict(const tracked_objects::Location& from_where,
    106                       const std::string& error_name,
    107                       scoped_ptr<base::DictionaryValue> error_data) {
    108   LOG(ERROR) << from_where.ToString() << ": " << error_name;
    109 }
    110 
    111 void LogErrorMessage(const tracked_objects::Location& from_where,
    112                      const std::string& error_name,
    113                      const std::string& error_message) {
    114   LOG(ERROR) << from_where.ToString() << ": " << error_message;
    115 }
    116 
    117 // Removes all kFakeCredential values from sensitive fields (determined by
    118 // onc::FieldIsCredential) of |onc_object|.
    119 void RemoveFakeCredentials(
    120     const onc::OncValueSignature& signature,
    121     base::DictionaryValue* onc_object) {
    122   base::DictionaryValue::Iterator it(*onc_object);
    123   while (!it.IsAtEnd()) {
    124     base::Value* value = NULL;
    125     std::string field_name = it.key();
    126     // We need the non-const entry to remove nested values but DictionaryValue
    127     // has no non-const iterator.
    128     onc_object->GetWithoutPathExpansion(field_name, &value);
    129     // Advance before delete.
    130     it.Advance();
    131 
    132     // If |value| is a dictionary, recurse.
    133     base::DictionaryValue* nested_object = NULL;
    134     if (value->GetAsDictionary(&nested_object)) {
    135       const onc::OncFieldSignature* field_signature =
    136           onc::GetFieldSignature(signature, field_name);
    137 
    138       RemoveFakeCredentials(*field_signature->value_signature,
    139                             nested_object);
    140       continue;
    141     }
    142 
    143     // If |value| is a string, check if it is a fake credential.
    144     std::string string_value;
    145     if (value->GetAsString(&string_value) &&
    146         onc::FieldIsCredential(signature, field_name)) {
    147       if (string_value == kFakeCredential) {
    148         // The value wasn't modified by the UI, thus we remove the field to keep
    149         // the existing value that is stored in Shill.
    150         onc_object->RemoveWithoutPathExpansion(field_name, NULL);
    151       }
    152       // Otherwise, the value is set and modified by the UI, thus we keep that
    153       // value to overwrite whatever is stored in Shill.
    154     }
    155   }
    156 }
    157 
    158 // Creates a Shill property dictionary from the given arguments. The resulting
    159 // dictionary will be sent to Shill by the caller. Depending on the profile
    160 // type, |policy| is interpreted as the user or device policy and |settings| as
    161 // the user or shared settings.
    162 scoped_ptr<base::DictionaryValue> CreateShillConfiguration(
    163     const NetworkProfile& profile,
    164     const std::string& guid,
    165     const base::DictionaryValue* policy,
    166     const base::DictionaryValue* settings) {
    167   scoped_ptr<base::DictionaryValue> effective;
    168   onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
    169   if (policy) {
    170     if (profile.type() == NetworkProfile::TYPE_SHARED) {
    171       effective = onc::MergeSettingsAndPoliciesToEffective(
    172           NULL,  // no user policy
    173           policy,  // device policy
    174           NULL,  // no user settings
    175           settings);  // shared settings
    176       onc_source = onc::ONC_SOURCE_DEVICE_POLICY;
    177     } else if (profile.type() == NetworkProfile::TYPE_USER) {
    178       effective = onc::MergeSettingsAndPoliciesToEffective(
    179           policy,  // user policy
    180           NULL,  // no device policy
    181           settings,  // user settings
    182           NULL);  // no shared settings
    183       onc_source = onc::ONC_SOURCE_USER_POLICY;
    184     } else {
    185       NOTREACHED();
    186     }
    187   } else if (settings) {
    188     effective.reset(settings->DeepCopy());
    189     // TODO(pneubeck): change to source ONC_SOURCE_USER
    190     onc_source = onc::ONC_SOURCE_NONE;
    191   } else {
    192     NOTREACHED();
    193     onc_source = onc::ONC_SOURCE_NONE;
    194   }
    195 
    196   RemoveFakeCredentials(onc::kNetworkConfigurationSignature,
    197                         effective.get());
    198 
    199   effective->SetStringWithoutPathExpansion(onc::network_config::kGUID, guid);
    200 
    201   // Remove irrelevant fields.
    202   onc::Normalizer normalizer(true /* remove recommended fields */);
    203   effective = normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
    204                                          *effective);
    205 
    206   scoped_ptr<base::DictionaryValue> shill_dictionary(
    207       onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
    208                                      *effective));
    209 
    210   shill_dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty,
    211                                                   profile.path);
    212 
    213   scoped_ptr<NetworkUIData> ui_data;
    214   if (policy)
    215     ui_data = NetworkUIData::CreateFromONC(onc_source, *policy);
    216   else
    217     ui_data.reset(new NetworkUIData());
    218 
    219   if (settings) {
    220     // Shill doesn't know that sensitive data is contained in the UIData
    221     // property and might write it into logs or other insecure places. Thus, we
    222     // have to remove or mask credentials.
    223     //
    224     // Shill's GetProperties doesn't return credentials. Masking credentials
    225     // instead of just removing them, allows remembering if a credential is set
    226     // or not.
    227     scoped_ptr<base::DictionaryValue> sanitized_settings(
    228         onc::MaskCredentialsInOncObject(onc::kNetworkConfigurationSignature,
    229                                         *settings,
    230                                         kFakeCredential));
    231     ui_data->set_user_settings(sanitized_settings.Pass());
    232   }
    233 
    234   SetUIData(*ui_data, shill_dictionary.get());
    235 
    236   VLOG(2) << "Created Shill properties: " << *shill_dictionary;
    237 
    238   return shill_dictionary.Pass();
    239 }
    240 
    241 // Returns true if |policy| matches |actual_network|, which must be part of a
    242 // ONC NetworkConfiguration. This should be the only such matching function
    243 // within Chrome. Shill does such matching in several functions for network
    244 // identification. For compatibility, we currently should stick to Shill's
    245 // matching behavior.
    246 bool IsPolicyMatching(const base::DictionaryValue& policy,
    247                       const base::DictionaryValue& actual_network) {
    248   std::string policy_type;
    249   policy.GetStringWithoutPathExpansion(onc::network_config::kType,
    250                                        &policy_type);
    251   std::string network_type;
    252   actual_network.GetStringWithoutPathExpansion(onc::network_config::kType,
    253                                                &network_type);
    254   if (policy_type != network_type)
    255     return false;
    256 
    257   if (network_type != onc::network_type::kWiFi)
    258     return false;
    259 
    260   const base::DictionaryValue* policy_wifi = NULL;
    261   policy.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi,
    262                                            &policy_wifi);
    263   const base::DictionaryValue* actual_wifi = NULL;
    264   actual_network.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi,
    265                                                    &actual_wifi);
    266   if (!policy_wifi || !actual_wifi)
    267     return false;
    268 
    269   std::string policy_ssid;
    270   policy_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &policy_ssid);
    271   std::string actual_ssid;
    272   actual_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &actual_ssid);
    273   return (policy_ssid == actual_ssid);
    274 }
    275 
    276 // Returns the policy from |policies| matching |actual_network|, if any exists.
    277 // Returns NULL otherwise. |actual_network| must be part of a ONC
    278 // NetworkConfiguration.
    279 const base::DictionaryValue* FindMatchingPolicy(
    280     const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies,
    281     const base::DictionaryValue& actual_network) {
    282   for (ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it =
    283            policies.begin(); it != policies.end(); ++it) {
    284     if (IsPolicyMatching(*it->second, actual_network))
    285       return it->second;
    286   }
    287   return NULL;
    288 }
    289 
    290 const base::DictionaryValue* GetByGUID(
    291     const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies,
    292     const std::string& guid) {
    293   ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it =
    294       policies.find(guid);
    295   if (it == policies.end())
    296     return NULL;
    297   return it->second;
    298 }
    299 
    300 void TranslatePropertiesToOncAndRunCallback(
    301     const network_handler::DictionaryResultCallback& callback,
    302     const std::string& service_path,
    303     const base::DictionaryValue& shill_properties) {
    304   scoped_ptr<base::DictionaryValue> onc_network(
    305       onc::TranslateShillServiceToONCPart(
    306           shill_properties,
    307           &onc::kNetworkWithStateSignature));
    308   callback.Run(service_path, *onc_network);
    309 }
    310 
    311 }  // namespace
    312 
    313 // This class compares (entry point is Run()) |modified_policies| with the
    314 // existing entries in the provided Shill profile |profile|. It fetches all
    315 // entries in parallel (GetProfilePropertiesCallback), compares each entry with
    316 // the current policies (GetEntryCallback) and adds all missing policies
    317 // (~PolicyApplicator).
    318 class ManagedNetworkConfigurationHandler::PolicyApplicator
    319     : public base::RefCounted<PolicyApplicator> {
    320  public:
    321   typedef ManagedNetworkConfigurationHandler::GuidToPolicyMap GuidToPolicyMap;
    322 
    323   // |modified_policies| must not be NULL and will be empty afterwards.
    324   PolicyApplicator(base::WeakPtr<ManagedNetworkConfigurationHandler> handler,
    325                    const NetworkProfile& profile,
    326                    std::set<std::string>* modified_policies);
    327 
    328   void Run();
    329 
    330  private:
    331   friend class base::RefCounted<PolicyApplicator>;
    332 
    333   // Called with the properties of the profile |profile_|. Requests the
    334   // properties of each entry, which are processed by GetEntryCallback.
    335   void GetProfilePropertiesCallback(
    336       const base::DictionaryValue& profile_properties);
    337 
    338   // Called with the properties of the profile entry |entry|. Checks whether the
    339   // entry was previously managed, whether a current policy applies and then
    340   // either updates, deletes or not touches the entry.
    341   void GetEntryCallback(const std::string& entry,
    342                         const base::DictionaryValue& entry_properties);
    343 
    344   // Sends Shill the command to delete profile entry |entry| from |profile_|.
    345   void DeleteEntry(const std::string& entry);
    346 
    347   // Creates new entries for all remaining policies, i.e. for which not matching
    348   // entry was found.
    349   virtual ~PolicyApplicator();
    350 
    351   std::set<std::string> remaining_policies_;
    352   base::WeakPtr<ManagedNetworkConfigurationHandler> handler_;
    353   NetworkProfile profile_;
    354 
    355   DISALLOW_COPY_AND_ASSIGN(PolicyApplicator);
    356 };
    357 
    358 // static
    359 scoped_ptr<NetworkUIData> ManagedNetworkConfigurationHandler::GetUIData(
    360     const base::DictionaryValue& shill_dictionary) {
    361   std::string ui_data_blob;
    362   if (shill_dictionary.GetStringWithoutPathExpansion(
    363           flimflam::kUIDataProperty,
    364           &ui_data_blob) &&
    365       !ui_data_blob.empty()) {
    366     scoped_ptr<base::DictionaryValue> ui_data_dict =
    367         onc::ReadDictionaryFromJson(ui_data_blob);
    368     if (ui_data_dict)
    369       return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
    370     else
    371       LOG(ERROR) << "UIData is not a valid JSON dictionary.";
    372   }
    373   VLOG(2) << "JSON dictionary has no UIData blob: " << shill_dictionary;
    374   return scoped_ptr<NetworkUIData>();
    375 }
    376 
    377 void ManagedNetworkConfigurationHandler::GetManagedProperties(
    378     const std::string& userhash,
    379     const std::string& service_path,
    380     const network_handler::DictionaryResultCallback& callback,
    381     const network_handler::ErrorCallback& error_callback) {
    382   if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) {
    383     RunErrorCallback(service_path,
    384                      kPoliciesNotInitialized,
    385                      kPoliciesNotInitializedMessage,
    386                      error_callback);
    387     return;
    388   }
    389   network_configuration_handler_->GetProperties(
    390       service_path,
    391       base::Bind(
    392           &ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback,
    393           weak_ptr_factory_.GetWeakPtr(),
    394           callback,
    395           error_callback),
    396       error_callback);
    397 }
    398 
    399 void ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback(
    400     const network_handler::DictionaryResultCallback& callback,
    401     const network_handler::ErrorCallback& error_callback,
    402     const std::string& service_path,
    403     const base::DictionaryValue& shill_properties) {
    404   std::string profile_path;
    405   shill_properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty,
    406                                                  &profile_path);
    407   LOG(ERROR) << "Profile: " << profile_path;
    408   const NetworkProfile* profile =
    409       network_profile_handler_->GetProfileForPath(profile_path);
    410   if (!profile) {
    411     LOG(ERROR) << "No or no known profile received for service "
    412             << service_path << ".";
    413   }
    414 
    415   scoped_ptr<NetworkUIData> ui_data = GetUIData(shill_properties);
    416 
    417   const base::DictionaryValue* user_settings = NULL;
    418   const base::DictionaryValue* shared_settings = NULL;
    419 
    420   if (ui_data && profile) {
    421     if (profile->type() == NetworkProfile::TYPE_SHARED)
    422       shared_settings = ui_data->user_settings();
    423     else if (profile->type() == NetworkProfile::TYPE_USER)
    424       user_settings = ui_data->user_settings();
    425     else
    426       NOTREACHED();
    427   } else if (profile) {
    428     LOG(WARNING) << "Service " << service_path << " of profile "
    429                  << profile_path << " contains no or no valid UIData.";
    430     // TODO(pneubeck): add a conversion of user configured entries of old
    431     // ChromeOS versions. We will have to use a heuristic to determine which
    432     // properties _might_ be user configured.
    433   }
    434 
    435   scoped_ptr<base::DictionaryValue> active_settings(
    436       onc::TranslateShillServiceToONCPart(
    437           shill_properties,
    438           &onc::kNetworkWithStateSignature));
    439 
    440   std::string guid;
    441   active_settings->GetStringWithoutPathExpansion(onc::network_config::kGUID,
    442                                                  &guid);
    443 
    444   const base::DictionaryValue* user_policy = NULL;
    445   const base::DictionaryValue* device_policy = NULL;
    446   if (!guid.empty() && profile) {
    447     const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile);
    448     if (!policies) {
    449       RunErrorCallback(service_path,
    450                        kPoliciesNotInitialized,
    451                        kPoliciesNotInitializedMessage,
    452                        error_callback);
    453       return;
    454     }
    455     const base::DictionaryValue* policy = GetByGUID(*policies, guid);
    456     if (profile->type() == NetworkProfile::TYPE_SHARED)
    457       device_policy = policy;
    458     else if (profile->type() == NetworkProfile::TYPE_USER)
    459       user_policy = policy;
    460     else
    461       NOTREACHED();
    462   }
    463 
    464   // This call also removes credentials from policies.
    465   scoped_ptr<base::DictionaryValue> augmented_properties =
    466       onc::MergeSettingsAndPoliciesToAugmented(
    467           onc::kNetworkConfigurationSignature,
    468           user_policy,
    469           device_policy,
    470           user_settings,
    471           shared_settings,
    472           active_settings.get());
    473   callback.Run(service_path, *augmented_properties);
    474 }
    475 
    476 void ManagedNetworkConfigurationHandler::GetProperties(
    477     const std::string& service_path,
    478     const network_handler::DictionaryResultCallback& callback,
    479     const network_handler::ErrorCallback& error_callback) const {
    480   network_configuration_handler_->GetProperties(
    481       service_path,
    482       base::Bind(&TranslatePropertiesToOncAndRunCallback, callback),
    483       error_callback);
    484 }
    485 
    486 void ManagedNetworkConfigurationHandler::SetProperties(
    487     const std::string& service_path,
    488     const base::DictionaryValue& user_settings,
    489     const base::Closure& callback,
    490     const network_handler::ErrorCallback& error_callback) const {
    491   const NetworkState* state =
    492       network_state_handler_->GetNetworkState(service_path);
    493 
    494   if (!state) {
    495     RunErrorCallback(service_path,
    496                      kUnknownServicePath,
    497                      kUnknownServicePathMessage,
    498                      error_callback);
    499     return;
    500   }
    501 
    502   std::string guid = state->guid();
    503   if (guid.empty()) {
    504     // TODO(pneubeck): create an initial configuration in this case. As for
    505     // CreateConfiguration, user settings from older ChromeOS versions have to
    506     // determined here.
    507     RunErrorCallback(service_path,
    508                      kSetOnUnconfiguredNetwork,
    509                      kSetOnUnconfiguredNetworkMessage,
    510                      error_callback);
    511     return;
    512   }
    513 
    514   const std::string& profile_path = state->profile_path();
    515   const NetworkProfile *profile =
    516       network_profile_handler_->GetProfileForPath(profile_path);
    517   if (!profile) {
    518     RunErrorCallback(service_path,
    519                      kUnknownProfilePath,
    520                      kUnknownProfilePathMessage,
    521                      error_callback);
    522     return;
    523   }
    524 
    525   VLOG(2) << "SetProperties: Found GUID " << guid << " and profile "
    526           << profile->ToDebugString();
    527 
    528   const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile);
    529   if (!policies) {
    530     RunErrorCallback(service_path,
    531                      kPoliciesNotInitialized,
    532                      kPoliciesNotInitializedMessage,
    533                      error_callback);
    534     return;
    535   }
    536 
    537   // Validate the ONC dictionary. We are liberal and ignore unknown field
    538   // names. User settings are only partial ONC, thus we ignore missing fields.
    539   onc::Validator validator(false,  // Ignore unknown fields.
    540                            false,  // Ignore invalid recommended field names.
    541                            false,  // Ignore missing fields.
    542                            false);  // This ONC does not come from policy.
    543 
    544   onc::Validator::Result validation_result;
    545   scoped_ptr<base::DictionaryValue> validated_user_settings =
    546       validator.ValidateAndRepairObject(
    547           &onc::kNetworkConfigurationSignature,
    548           user_settings,
    549           &validation_result);
    550 
    551   if (validation_result == onc::Validator::INVALID) {
    552     RunErrorCallback(service_path,
    553                      kInvalidUserSettings,
    554                      kInvalidUserSettingsMessage,
    555                      error_callback);
    556     return;
    557   }
    558   if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
    559     LOG(WARNING) << "Validation of ONC user settings produced warnings.";
    560 
    561   const base::DictionaryValue* policy = GetByGUID(*policies, guid);
    562   VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed.";
    563 
    564   scoped_ptr<base::DictionaryValue> shill_dictionary(CreateShillConfiguration(
    565       *profile, guid, policy, validated_user_settings.get()));
    566 
    567   network_configuration_handler_->SetProperties(
    568       service_path, *shill_dictionary, callback, error_callback);
    569 }
    570 
    571 void ManagedNetworkConfigurationHandler::CreateConfiguration(
    572     const std::string& userhash,
    573     const base::DictionaryValue& properties,
    574     const network_handler::StringResultCallback& callback,
    575     const network_handler::ErrorCallback& error_callback) const {
    576   const GuidToPolicyMap* policies = GetPoliciesForUser(userhash);
    577   if (!policies) {
    578     RunErrorCallback("",
    579                      kPoliciesNotInitialized,
    580                      kPoliciesNotInitializedMessage,
    581                      error_callback);
    582     return;
    583   }
    584 
    585   if (FindMatchingPolicy(*policies, properties)) {
    586     RunErrorCallback("",
    587                      kNetworkAlreadyConfigured,
    588                      kNetworkAlreadyConfiguredMessage,
    589                      error_callback);
    590   }
    591 
    592   const NetworkProfile* profile =
    593       network_profile_handler_->GetProfileForUserhash(userhash);
    594   if (!profile) {
    595     RunErrorCallback("",
    596                      kProfileNotInitialized,
    597                      kProfileNotInitializedMessage,
    598                      error_callback);
    599   }
    600 
    601   // TODO(pneubeck): In case of WiFi, check that no other configuration for the
    602   // same {SSID, mode, security} exists. We don't support such multiple
    603   // configurations, yet.
    604 
    605   // Generate a new GUID for this configuration. Ignore the maybe provided GUID
    606   // in |properties| as it is not our own and from an untrusted source.
    607   std::string guid = base::GenerateGUID();
    608   scoped_ptr<base::DictionaryValue> shill_dictionary(
    609       CreateShillConfiguration(*profile, guid, NULL /*no policy*/,
    610                                &properties));
    611 
    612   network_configuration_handler_->CreateConfiguration(
    613       *shill_dictionary, callback, error_callback);
    614 }
    615 
    616 void ManagedNetworkConfigurationHandler::RemoveConfiguration(
    617     const std::string& service_path,
    618     const base::Closure& callback,
    619     const network_handler::ErrorCallback& error_callback) const {
    620   network_configuration_handler_->RemoveConfiguration(
    621       service_path, callback, error_callback);
    622 }
    623 
    624 void ManagedNetworkConfigurationHandler::SetPolicy(
    625     onc::ONCSource onc_source,
    626     const std::string& userhash,
    627     const base::ListValue& network_configs_onc) {
    628   VLOG(1) << "Setting policies from " << ToDebugString(onc_source, userhash)
    629           << ".";
    630 
    631   // |userhash| must be empty for device policies.
    632   DCHECK(onc_source != chromeos::onc::ONC_SOURCE_DEVICE_POLICY ||
    633          userhash.empty());
    634   GuidToPolicyMap& policies = policies_by_user_[userhash];
    635 
    636   GuidToPolicyMap old_policies;
    637   policies.swap(old_policies);
    638 
    639   // This stores all GUIDs of policies that have changed or are new.
    640   std::set<std::string> modified_policies;
    641 
    642   for (base::ListValue::const_iterator it = network_configs_onc.begin();
    643        it != network_configs_onc.end(); ++it) {
    644     const base::DictionaryValue* network = NULL;
    645     (*it)->GetAsDictionary(&network);
    646     DCHECK(network);
    647 
    648     std::string guid;
    649     network->GetStringWithoutPathExpansion(onc::network_config::kGUID, &guid);
    650     DCHECK(!guid.empty());
    651 
    652     if (policies.count(guid) > 0) {
    653       LOG(ERROR) << "ONC from " << ToDebugString(onc_source, userhash)
    654                  << " contains several entries for the same GUID "
    655                  << guid << ".";
    656       delete policies[guid];
    657     }
    658     const base::DictionaryValue* new_entry = network->DeepCopy();
    659     policies[guid] = new_entry;
    660 
    661     const base::DictionaryValue* old_entry = old_policies[guid];
    662     if (!old_entry || !old_entry->Equals(new_entry))
    663       modified_policies.insert(guid);
    664   }
    665 
    666   STLDeleteValues(&old_policies);
    667 
    668   const NetworkProfile* profile =
    669       network_profile_handler_->GetProfileForUserhash(userhash);
    670   if (!profile) {
    671     VLOG(1) << "The relevant Shill profile isn't initialized yet, postponing "
    672             << "policy application.";
    673     return;
    674   }
    675 
    676   scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
    677       weak_ptr_factory_.GetWeakPtr(),
    678       *profile,
    679       &modified_policies);
    680   applicator->Run();
    681 }
    682 
    683 void ManagedNetworkConfigurationHandler::OnProfileAdded(
    684     const NetworkProfile& profile) {
    685   VLOG(1) << "Adding profile " << profile.ToDebugString() << "'.";
    686 
    687   const GuidToPolicyMap* policies = GetPoliciesForProfile(profile);
    688   if (!policies) {
    689     VLOG(1) << "The relevant policy is not initialized, "
    690             << "postponing policy application.";
    691     return;
    692   }
    693 
    694   std::set<std::string> policy_guids;
    695   for (GuidToPolicyMap::const_iterator it = policies->begin();
    696        it != policies->end(); ++it) {
    697     policy_guids.insert(it->first);
    698   }
    699 
    700   scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
    701       weak_ptr_factory_.GetWeakPtr(),
    702       profile,
    703       &policy_guids);
    704   applicator->Run();
    705 }
    706 
    707 const base::DictionaryValue*
    708 ManagedNetworkConfigurationHandler::FindPolicyByGUID(
    709     const std::string userhash,
    710     const std::string& guid,
    711     onc::ONCSource* onc_source) const {
    712   *onc_source = onc::ONC_SOURCE_NONE;
    713 
    714   if (!userhash.empty()) {
    715     const GuidToPolicyMap* user_policies = GetPoliciesForUser(userhash);
    716     if (user_policies) {
    717       GuidToPolicyMap::const_iterator found = user_policies->find(guid);
    718       if (found != user_policies->end()) {
    719         *onc_source = onc::ONC_SOURCE_USER_POLICY;
    720         return found->second;
    721       }
    722     }
    723   }
    724 
    725   const GuidToPolicyMap* device_policies = GetPoliciesForUser(std::string());
    726   if (device_policies) {
    727     GuidToPolicyMap::const_iterator found = device_policies->find(guid);
    728     if (found != device_policies->end()) {
    729       *onc_source = onc::ONC_SOURCE_DEVICE_POLICY;
    730       return found->second;
    731     }
    732   }
    733 
    734   return NULL;
    735 }
    736 
    737 void ManagedNetworkConfigurationHandler::OnProfileRemoved(
    738     const NetworkProfile& profile) {
    739   // Nothing to do in this case.
    740 }
    741 
    742 const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
    743 ManagedNetworkConfigurationHandler::GetPoliciesForUser(
    744     const std::string& userhash) const {
    745   UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash);
    746   if (it == policies_by_user_.end())
    747     return NULL;
    748   return &it->second;
    749 }
    750 
    751 const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
    752 ManagedNetworkConfigurationHandler::GetPoliciesForProfile(
    753     const NetworkProfile& profile) const {
    754   DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
    755          profile.userhash.empty());
    756   return GetPoliciesForUser(profile.userhash);
    757 }
    758 
    759 ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler()
    760     : network_state_handler_(NULL),
    761       network_profile_handler_(NULL),
    762       network_configuration_handler_(NULL),
    763       weak_ptr_factory_(this) {
    764 }
    765 
    766 ManagedNetworkConfigurationHandler::~ManagedNetworkConfigurationHandler() {
    767   network_profile_handler_->RemoveObserver(this);
    768   for (UserToPoliciesMap::iterator it = policies_by_user_.begin();
    769        it != policies_by_user_.end(); ++it) {
    770     STLDeleteValues(&it->second);
    771   }
    772 }
    773 
    774 void ManagedNetworkConfigurationHandler::Init(
    775     NetworkStateHandler* network_state_handler,
    776     NetworkProfileHandler* network_profile_handler,
    777     NetworkConfigurationHandler* network_configuration_handler) {
    778   network_state_handler_ = network_state_handler;
    779   network_profile_handler_ = network_profile_handler;
    780   network_configuration_handler_ = network_configuration_handler;
    781   network_profile_handler_->AddObserver(this);
    782 }
    783 
    784 ManagedNetworkConfigurationHandler::PolicyApplicator::PolicyApplicator(
    785     base::WeakPtr<ManagedNetworkConfigurationHandler> handler,
    786     const NetworkProfile& profile,
    787     std::set<std::string>* modified_policies)
    788     : handler_(handler), profile_(profile) {
    789   remaining_policies_.swap(*modified_policies);
    790 }
    791 
    792 void ManagedNetworkConfigurationHandler::PolicyApplicator::Run() {
    793   DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
    794       dbus::ObjectPath(profile_.path),
    795       base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
    796       base::Bind(&LogErrorMessage, FROM_HERE));
    797 }
    798 
    799 void ManagedNetworkConfigurationHandler::PolicyApplicator::
    800     GetProfilePropertiesCallback(
    801         const base::DictionaryValue& profile_properties) {
    802   if (!handler_) {
    803     LOG(WARNING) << "Handler destructed during policy application to profile "
    804                  << profile_.ToDebugString();
    805     return;
    806   }
    807 
    808   VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
    809   const base::ListValue* entries = NULL;
    810   if (!profile_properties.GetListWithoutPathExpansion(
    811           flimflam::kEntriesProperty, &entries)) {
    812     LOG(ERROR) << "Profile " << profile_.ToDebugString()
    813                << " doesn't contain the property "
    814                << flimflam::kEntriesProperty;
    815     return;
    816   }
    817 
    818   for (base::ListValue::const_iterator it = entries->begin();
    819        it != entries->end();
    820        ++it) {
    821     std::string entry;
    822     (*it)->GetAsString(&entry);
    823 
    824     std::ostringstream entry_failure;
    825     DBusThreadManager::Get()->GetShillProfileClient()
    826         ->GetEntry(dbus::ObjectPath(profile_.path),
    827                    entry,
    828                    base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
    829                    base::Bind(&LogErrorMessage, FROM_HERE));
    830   }
    831 }
    832 
    833 void ManagedNetworkConfigurationHandler::PolicyApplicator::GetEntryCallback(
    834     const std::string& entry,
    835     const base::DictionaryValue& entry_properties) {
    836   if (!handler_) {
    837     LOG(WARNING) << "Handler destructed during policy application to profile "
    838                  << profile_.ToDebugString();
    839     return;
    840   }
    841 
    842   VLOG(2) << "Received properties for entry " << entry << " of profile "
    843           << profile_.ToDebugString();
    844 
    845   scoped_ptr<base::DictionaryValue> onc_part(
    846       onc::TranslateShillServiceToONCPart(entry_properties,
    847                                           &onc::kNetworkWithStateSignature));
    848 
    849   std::string old_guid;
    850   if (!onc_part->GetStringWithoutPathExpansion(onc::network_config::kGUID,
    851                                                &old_guid)) {
    852     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
    853             << " doesn't contain a GUID.";
    854     // This might be an entry of an older ChromeOS version. Assume it to be
    855     // unmanaged.
    856   }
    857 
    858   scoped_ptr<NetworkUIData> ui_data = GetUIData(entry_properties);
    859   if (!ui_data) {
    860     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
    861             << " contains no or no valid UIData.";
    862     // This might be an entry of an older ChromeOS version. Assume it to be
    863     // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
    864     // clear the GUID just in case.
    865     old_guid.clear();
    866   }
    867 
    868   bool was_managed = !old_guid.empty() && ui_data &&
    869                      (ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY ||
    870                       ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY);
    871 
    872   // The relevant policy must have been initialized, otherwise we hadn't Run
    873   // this PolicyApplicator.
    874   const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_);
    875 
    876   const base::DictionaryValue* new_policy = NULL;
    877   if (was_managed) {
    878     // If we have a GUID that might match a current policy, do a lookup using
    879     // that GUID at first. In particular this is necessary, as some networks
    880     // can't be matched to policies by properties (e.g. VPN).
    881     new_policy = GetByGUID(policies, old_guid);
    882   }
    883 
    884   if (!new_policy) {
    885     // If we didn't find a policy by GUID, still a new policy might match.
    886     new_policy = FindMatchingPolicy(policies, *onc_part);
    887   }
    888 
    889   if (new_policy) {
    890     std::string new_guid;
    891     new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID,
    892                                               &new_guid);
    893 
    894     VLOG_IF(1, was_managed && old_guid != new_guid)
    895         << "Updating configuration previously managed by policy " << old_guid
    896         << " with new policy " << new_guid << ".";
    897     VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
    898                              << " to previously unmanaged "
    899                              << "configuration.";
    900 
    901     if (old_guid == new_guid &&
    902         remaining_policies_.find(new_guid) == remaining_policies_.end()) {
    903       VLOG(1) << "Not updating existing managed configuration with guid "
    904               << new_guid << " because the policy didn't change.";
    905     } else {
    906       // Delete the entry to ensure that no old configuration remains.
    907       // Don't do this if a policy is reapplied (e.g. after reboot) or updated
    908       // (i.e. the GUID didn't change), in order to keep implicit state of
    909       // Shill like "connected successfully before".
    910       if (old_guid == new_guid) {
    911         VLOG(1) << "Updating previously managed configuration with the "
    912                 << "updated policy " << new_guid << ".";
    913       } else {
    914         DeleteEntry(entry);
    915       }
    916 
    917       const base::DictionaryValue* user_settings =
    918           ui_data ? ui_data->user_settings() : NULL;
    919 
    920       // Write the new configuration.
    921       scoped_ptr<base::DictionaryValue> shill_dictionary =
    922           CreateShillConfiguration(
    923               profile_, new_guid, new_policy, user_settings);
    924       handler_->network_configuration_handler()
    925           ->CreateConfiguration(*shill_dictionary,
    926                                 base::Bind(&IgnoreString),
    927                                 base::Bind(&LogErrorWithDict, FROM_HERE));
    928       remaining_policies_.erase(new_guid);
    929     }
    930   } else if (was_managed) {
    931     VLOG(1) << "Removing configuration previously managed by policy "
    932             << old_guid << ", because the policy was removed.";
    933 
    934     // Remove the entry, because the network was managed but isn't anymore.
    935     // Note: An alternative might be to preserve the user settings, but it's
    936     // unclear which values originating the policy should be removed.
    937     DeleteEntry(entry);
    938   } else {
    939     VLOG(2) << "Ignore unmanaged entry.";
    940 
    941     // The entry wasn't managed and doesn't match any current policy. Thus
    942     // leave it as it is.
    943   }
    944 }
    945 
    946 void ManagedNetworkConfigurationHandler::PolicyApplicator::DeleteEntry(
    947     const std::string& entry) {
    948   DBusThreadManager::Get()->GetShillProfileClient()
    949       ->DeleteEntry(dbus::ObjectPath(profile_.path),
    950                     entry,
    951                     base::Bind(&base::DoNothing),
    952                     base::Bind(&LogErrorMessage, FROM_HERE));
    953 }
    954 
    955 ManagedNetworkConfigurationHandler::PolicyApplicator::~PolicyApplicator() {
    956   if (!handler_) {
    957     LOG(WARNING) << "Handler destructed during policy application to profile "
    958                  << profile_.ToDebugString();
    959     return;
    960   }
    961 
    962   if (remaining_policies_.empty())
    963     return;
    964 
    965   VLOG(2) << "Create new managed network configurations in profile"
    966           << profile_.ToDebugString() << ".";
    967   // All profile entries were compared to policies. |configureGUIDs_| contains
    968   // all matched policies. From the remainder of policies, new configurations
    969   // have to be created.
    970 
    971   // The relevant policy must have been initialized, otherwise we hadn't Run
    972   // this PolicyApplicator.
    973   const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_);
    974 
    975   for (std::set<std::string>::iterator it = remaining_policies_.begin();
    976        it != remaining_policies_.end();
    977        ++it) {
    978     const base::DictionaryValue* policy = GetByGUID(policies, *it);
    979     if (!policy) {
    980       LOG(ERROR) << "Policy " << *it << " doesn't exist anymore.";
    981       continue;
    982     }
    983 
    984     VLOG(1) << "Creating new configuration managed by policy " << *it
    985             << " in profile " << profile_.ToDebugString() << ".";
    986 
    987     scoped_ptr<base::DictionaryValue> shill_dictionary =
    988         CreateShillConfiguration(
    989             profile_, *it, policy, NULL /* no user settings */);
    990     handler_->network_configuration_handler()
    991         ->CreateConfiguration(*shill_dictionary,
    992                               base::Bind(&IgnoreString),
    993                               base::Bind(&LogErrorWithDict, FROM_HERE));
    994   }
    995 }
    996 
    997 }  // namespace chromeos
    998