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