Home | History | Annotate | Download | only in network
      1 // Copyright 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/policy_applicator.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/location.h"
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/stl_util.h"
     14 #include "base/values.h"
     15 #include "chromeos/dbus/dbus_thread_manager.h"
     16 #include "chromeos/dbus/shill_profile_client.h"
     17 #include "chromeos/network/network_ui_data.h"
     18 #include "chromeos/network/onc/onc_signature.h"
     19 #include "chromeos/network/onc/onc_translator.h"
     20 #include "chromeos/network/policy_util.h"
     21 #include "chromeos/network/shill_property_util.h"
     22 #include "components/onc/onc_constants.h"
     23 #include "dbus/object_path.h"
     24 #include "third_party/cros_system_api/dbus/service_constants.h"
     25 
     26 namespace chromeos {
     27 
     28 namespace {
     29 
     30 void LogErrorMessage(const tracked_objects::Location& from_where,
     31                      const std::string& error_name,
     32                      const std::string& error_message) {
     33   LOG(ERROR) << from_where.ToString() << ": " << error_message;
     34 }
     35 
     36 const base::DictionaryValue* GetByGUID(
     37     const PolicyApplicator::GuidToPolicyMap& policies,
     38     const std::string& guid) {
     39   PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
     40   if (it == policies.end())
     41     return NULL;
     42   return it->second;
     43 }
     44 
     45 }  // namespace
     46 
     47 PolicyApplicator::PolicyApplicator(
     48     base::WeakPtr<ConfigurationHandler> handler,
     49     const NetworkProfile& profile,
     50     const GuidToPolicyMap& all_policies,
     51     const base::DictionaryValue& global_network_config,
     52     std::set<std::string>* modified_policies)
     53     : handler_(handler), profile_(profile) {
     54   global_network_config_.MergeDictionary(&global_network_config);
     55   remaining_policies_.swap(*modified_policies);
     56   for (GuidToPolicyMap::const_iterator it = all_policies.begin();
     57        it != all_policies.end(); ++it) {
     58     all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
     59   }
     60 }
     61 
     62 void PolicyApplicator::Run() {
     63   DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
     64       dbus::ObjectPath(profile_.path),
     65       base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
     66       base::Bind(&LogErrorMessage, FROM_HERE));
     67 }
     68 
     69 void PolicyApplicator::GetProfilePropertiesCallback(
     70     const base::DictionaryValue& profile_properties) {
     71   if (!handler_) {
     72     LOG(WARNING) << "Handler destructed during policy application to profile "
     73                  << profile_.ToDebugString();
     74     return;
     75   }
     76 
     77   VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
     78   const base::ListValue* entries = NULL;
     79   if (!profile_properties.GetListWithoutPathExpansion(
     80            shill::kEntriesProperty, &entries)) {
     81     LOG(ERROR) << "Profile " << profile_.ToDebugString()
     82                << " doesn't contain the property "
     83                << shill::kEntriesProperty;
     84     return;
     85   }
     86 
     87   for (base::ListValue::const_iterator it = entries->begin();
     88        it != entries->end(); ++it) {
     89     std::string entry;
     90     (*it)->GetAsString(&entry);
     91 
     92     DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
     93         dbus::ObjectPath(profile_.path),
     94         entry,
     95         base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
     96         base::Bind(&LogErrorMessage, FROM_HERE));
     97   }
     98 }
     99 
    100 void PolicyApplicator::GetEntryCallback(
    101     const std::string& entry,
    102     const base::DictionaryValue& entry_properties) {
    103   if (!handler_) {
    104     LOG(WARNING) << "Handler destructed during policy application to profile "
    105                  << profile_.ToDebugString();
    106     return;
    107   }
    108 
    109   VLOG(2) << "Received properties for entry " << entry << " of profile "
    110           << profile_.ToDebugString();
    111 
    112   scoped_ptr<base::DictionaryValue> onc_part(
    113       onc::TranslateShillServiceToONCPart(entry_properties,
    114                                           &onc::kNetworkWithStateSignature));
    115 
    116   std::string old_guid;
    117   if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
    118                                                &old_guid)) {
    119     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
    120             << " doesn't contain a GUID.";
    121     // This might be an entry of an older ChromeOS version. Assume it to be
    122     // unmanaged.
    123   }
    124 
    125   scoped_ptr<NetworkUIData> ui_data =
    126       shill_property_util::GetUIDataFromProperties(entry_properties);
    127   if (!ui_data) {
    128     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
    129             << " contains no or no valid UIData.";
    130     // This might be an entry of an older ChromeOS version. Assume it to be
    131     // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
    132     // clear the GUID just in case.
    133     old_guid.clear();
    134   }
    135 
    136   bool was_managed = !old_guid.empty() && ui_data &&
    137                      (ui_data->onc_source() ==
    138                           ::onc::ONC_SOURCE_DEVICE_POLICY ||
    139                       ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
    140 
    141   const base::DictionaryValue* new_policy = NULL;
    142   if (was_managed) {
    143     // If we have a GUID that might match a current policy, do a lookup using
    144     // that GUID at first. In particular this is necessary, as some networks
    145     // can't be matched to policies by properties (e.g. VPN).
    146     new_policy = GetByGUID(all_policies_, old_guid);
    147   }
    148 
    149   if (!new_policy) {
    150     // If we didn't find a policy by GUID, still a new policy might match.
    151     new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
    152   }
    153 
    154   if (new_policy) {
    155     std::string new_guid;
    156     new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
    157                                               &new_guid);
    158 
    159     VLOG_IF(1, was_managed && old_guid != new_guid)
    160         << "Updating configuration previously managed by policy " << old_guid
    161         << " with new policy " << new_guid << ".";
    162     VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
    163                              << " to previously unmanaged "
    164                              << "configuration.";
    165 
    166     if (old_guid == new_guid &&
    167         remaining_policies_.find(new_guid) == remaining_policies_.end()) {
    168       VLOG(1) << "Not updating existing managed configuration with guid "
    169               << new_guid << " because the policy didn't change.";
    170     } else {
    171       const base::DictionaryValue* user_settings =
    172           ui_data ? ui_data->user_settings() : NULL;
    173       scoped_ptr<base::DictionaryValue> new_shill_properties =
    174           policy_util::CreateShillConfiguration(
    175               profile_, new_guid, new_policy, user_settings);
    176       // A new policy has to be applied to this profile entry. In order to keep
    177       // implicit state of Shill like "connected successfully before", keep the
    178       // entry if a policy is reapplied (e.g. after reboot) or is updated.
    179       // However, some Shill properties are used to identify the network and
    180       // cannot be modified after initial configuration, so we have to delete
    181       // the profile entry in these cases. Also, keeping Shill's state if the
    182       // SSID changed might not be a good idea anyways. If the policy GUID
    183       // changed, or there was no policy before, we delete the entry at first to
    184       // ensure that no old configuration remains.
    185       if (old_guid == new_guid &&
    186           shill_property_util::DoIdentifyingPropertiesMatch(
    187               *new_shill_properties, entry_properties)) {
    188         VLOG(1) << "Updating previously managed configuration with the "
    189                 << "updated policy " << new_guid << ".";
    190       } else {
    191         VLOG(1) << "Deleting profile entry before writing new policy "
    192                 << new_guid << " because of identifying properties changed.";
    193         DeleteEntry(entry);
    194       }
    195 
    196       WriteNewShillConfiguration(*new_shill_properties, *new_policy);
    197       remaining_policies_.erase(new_guid);
    198     }
    199   } else if (was_managed) {
    200     VLOG(1) << "Removing configuration previously managed by policy "
    201             << old_guid << ", because the policy was removed.";
    202 
    203     // Remove the entry, because the network was managed but isn't anymore.
    204     // Note: An alternative might be to preserve the user settings, but it's
    205     // unclear which values originating the policy should be removed.
    206     DeleteEntry(entry);
    207   } else {
    208     // The entry wasn't managed and doesn't match any current policy. Global
    209     // network settings have to be applied.
    210     base::DictionaryValue shill_properties_to_update;
    211     GetPropertiesForUnmanagedEntry(entry_properties,
    212                                    &shill_properties_to_update);
    213     if (shill_properties_to_update.empty()) {
    214       VLOG(2) << "Ignore unmanaged entry.";
    215       // Calling a SetProperties of Shill with an empty dictionary is a no op.
    216     } else {
    217       VLOG(2) << "Apply global network config to unmanaged entry.";
    218       handler_->UpdateExistingConfigurationWithPropertiesFromPolicy(
    219           entry_properties, shill_properties_to_update);
    220     }
    221   }
    222 }
    223 
    224 void PolicyApplicator::DeleteEntry(const std::string& entry) {
    225   DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
    226       dbus::ObjectPath(profile_.path),
    227       entry,
    228       base::Bind(&base::DoNothing),
    229       base::Bind(&LogErrorMessage, FROM_HERE));
    230 }
    231 
    232 void PolicyApplicator::WriteNewShillConfiguration(
    233     const base::DictionaryValue& shill_dictionary,
    234     const base::DictionaryValue& policy) {
    235   // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
    236   // user. Abort in that case.
    237   std::string type;
    238   policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
    239   if (type == ::onc::network_type::kEthernet &&
    240       profile_.type() == NetworkProfile::TYPE_USER) {
    241     const base::DictionaryValue* ethernet = NULL;
    242     policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
    243                                              &ethernet);
    244     std::string auth;
    245     ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
    246                                             &auth);
    247     if (auth == ::onc::ethernet::kNone)
    248       return;
    249   }
    250 
    251   handler_->CreateConfigurationFromPolicy(shill_dictionary);
    252 }
    253 
    254 void PolicyApplicator::GetPropertiesForUnmanagedEntry(
    255     const base::DictionaryValue& entry_properties,
    256     base::DictionaryValue* properties_to_update) const {
    257   // kAllowOnlyPolicyNetworksToAutoconnect is currently the only global config.
    258 
    259   std::string type;
    260   entry_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    261   if (NetworkTypePattern::Ethernet().MatchesType(type))
    262     return;  // Autoconnect for Ethernet cannot be configured.
    263 
    264   // By default all networks are allowed to autoconnect.
    265   bool only_policy_autoconnect = false;
    266   global_network_config_.GetBooleanWithoutPathExpansion(
    267       ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
    268       &only_policy_autoconnect);
    269   if (!only_policy_autoconnect)
    270     return;
    271 
    272   bool old_autoconnect = false;
    273   if (entry_properties.GetBooleanWithoutPathExpansion(
    274           shill::kAutoConnectProperty, &old_autoconnect) &&
    275       !old_autoconnect) {
    276     // Autoconnect is already explictly disabled. No need to set it again.
    277     return;
    278   }
    279   // If autconnect is not explicitly set yet, it might automatically be enabled
    280   // by Shill. To prevent that, disable it explicitly.
    281   properties_to_update->SetBooleanWithoutPathExpansion(
    282       shill::kAutoConnectProperty, false);
    283 }
    284 
    285 PolicyApplicator::~PolicyApplicator() {
    286   ApplyRemainingPolicies();
    287   STLDeleteValues(&all_policies_);
    288 }
    289 
    290 void PolicyApplicator::ApplyRemainingPolicies() {
    291   if (!handler_) {
    292     LOG(WARNING) << "Handler destructed during policy application to profile "
    293                  << profile_.ToDebugString();
    294     return;
    295   }
    296 
    297   if (remaining_policies_.empty())
    298     return;
    299 
    300   VLOG(2) << "Create new managed network configurations in profile"
    301           << profile_.ToDebugString() << ".";
    302   // All profile entries were compared to policies. |remaining_policies_|
    303   // contains all modified policies that didn't match any entry. For these
    304   // remaining policies, new configurations have to be created.
    305   for (std::set<std::string>::iterator it = remaining_policies_.begin();
    306        it != remaining_policies_.end(); ++it) {
    307     const base::DictionaryValue* policy = GetByGUID(all_policies_, *it);
    308     DCHECK(policy);
    309 
    310     VLOG(1) << "Creating new configuration managed by policy " << *it
    311             << " in profile " << profile_.ToDebugString() << ".";
    312 
    313     scoped_ptr<base::DictionaryValue> shill_dictionary =
    314         policy_util::CreateShillConfiguration(profile_, *it, policy, NULL);
    315     WriteNewShillConfiguration(*shill_dictionary, *policy);
    316   }
    317 }
    318 
    319 }  // namespace chromeos
    320