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 ðernet); 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