policy_applicator.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/stl_util.h" 13#include "base/values.h" 14#include "chromeos/dbus/dbus_thread_manager.h" 15#include "chromeos/dbus/shill_profile_client.h" 16#include "chromeos/network/network_ui_data.h" 17#include "chromeos/network/onc/onc_signature.h" 18#include "chromeos/network/onc/onc_translator.h" 19#include "chromeos/network/policy_util.h" 20#include "chromeos/network/shill_property_util.h" 21#include "components/onc/onc_constants.h" 22#include "dbus/object_path.h" 23#include "third_party/cros_system_api/dbus/service_constants.h" 24 25namespace chromeos { 26 27namespace { 28 29void LogErrorMessage(const tracked_objects::Location& from_where, 30 const std::string& error_name, 31 const std::string& error_message) { 32 LOG(ERROR) << from_where.ToString() << ": " << error_message; 33} 34 35const base::DictionaryValue* GetByGUID( 36 const PolicyApplicator::GuidToPolicyMap& policies, 37 const std::string& guid) { 38 PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid); 39 if (it == policies.end()) 40 return NULL; 41 return it->second; 42} 43 44} // namespace 45 46PolicyApplicator::PolicyApplicator(base::WeakPtr<ConfigurationHandler> handler, 47 const NetworkProfile& profile, 48 const GuidToPolicyMap& all_policies, 49 std::set<std::string>* modified_policies) 50 : handler_(handler), profile_(profile) { 51 remaining_policies_.swap(*modified_policies); 52 for (GuidToPolicyMap::const_iterator it = all_policies.begin(); 53 it != all_policies.end(); ++it) { 54 all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy())); 55 } 56} 57 58void PolicyApplicator::Run() { 59 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties( 60 dbus::ObjectPath(profile_.path), 61 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this), 62 base::Bind(&LogErrorMessage, FROM_HERE)); 63} 64 65void PolicyApplicator::GetProfilePropertiesCallback( 66 const base::DictionaryValue& profile_properties) { 67 if (!handler_) { 68 LOG(WARNING) << "Handler destructed during policy application to profile " 69 << profile_.ToDebugString(); 70 return; 71 } 72 73 VLOG(2) << "Received properties for profile " << profile_.ToDebugString(); 74 const base::ListValue* entries = NULL; 75 if (!profile_properties.GetListWithoutPathExpansion( 76 shill::kEntriesProperty, &entries)) { 77 LOG(ERROR) << "Profile " << profile_.ToDebugString() 78 << " doesn't contain the property " 79 << shill::kEntriesProperty; 80 return; 81 } 82 83 for (base::ListValue::const_iterator it = entries->begin(); 84 it != entries->end(); ++it) { 85 std::string entry; 86 (*it)->GetAsString(&entry); 87 88 DBusThreadManager::Get()->GetShillProfileClient()->GetEntry( 89 dbus::ObjectPath(profile_.path), 90 entry, 91 base::Bind(&PolicyApplicator::GetEntryCallback, this, entry), 92 base::Bind(&LogErrorMessage, FROM_HERE)); 93 } 94} 95 96void PolicyApplicator::GetEntryCallback( 97 const std::string& entry, 98 const base::DictionaryValue& entry_properties) { 99 if (!handler_) { 100 LOG(WARNING) << "Handler destructed during policy application to profile " 101 << profile_.ToDebugString(); 102 return; 103 } 104 105 VLOG(2) << "Received properties for entry " << entry << " of profile " 106 << profile_.ToDebugString(); 107 108 scoped_ptr<base::DictionaryValue> onc_part( 109 onc::TranslateShillServiceToONCPart(entry_properties, 110 &onc::kNetworkWithStateSignature)); 111 112 std::string old_guid; 113 if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID, 114 &old_guid)) { 115 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString() 116 << " doesn't contain a GUID."; 117 // This might be an entry of an older ChromeOS version. Assume it to be 118 // unmanaged. 119 } 120 121 scoped_ptr<NetworkUIData> ui_data = 122 shill_property_util::GetUIDataFromProperties(entry_properties); 123 if (!ui_data) { 124 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString() 125 << " contains no or no valid UIData."; 126 // This might be an entry of an older ChromeOS version. Assume it to be 127 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus 128 // clear the GUID just in case. 129 old_guid.clear(); 130 } 131 132 bool was_managed = !old_guid.empty() && ui_data && 133 (ui_data->onc_source() == 134 ::onc::ONC_SOURCE_DEVICE_POLICY || 135 ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY); 136 137 const base::DictionaryValue* new_policy = NULL; 138 if (was_managed) { 139 // If we have a GUID that might match a current policy, do a lookup using 140 // that GUID at first. In particular this is necessary, as some networks 141 // can't be matched to policies by properties (e.g. VPN). 142 new_policy = GetByGUID(all_policies_, old_guid); 143 } 144 145 if (!new_policy) { 146 // If we didn't find a policy by GUID, still a new policy might match. 147 new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part); 148 } 149 150 if (new_policy) { 151 std::string new_guid; 152 new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID, 153 &new_guid); 154 155 VLOG_IF(1, was_managed && old_guid != new_guid) 156 << "Updating configuration previously managed by policy " << old_guid 157 << " with new policy " << new_guid << "."; 158 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid 159 << " to previously unmanaged " 160 << "configuration."; 161 162 if (old_guid == new_guid && 163 remaining_policies_.find(new_guid) == remaining_policies_.end()) { 164 VLOG(1) << "Not updating existing managed configuration with guid " 165 << new_guid << " because the policy didn't change."; 166 } else { 167 // Delete the entry to ensure that no old configuration remains. 168 // Don't do this if a policy is reapplied (e.g. after reboot) or updated 169 // (i.e. the GUID didn't change), in order to keep implicit state of 170 // Shill like "connected successfully before". 171 if (old_guid == new_guid) { 172 VLOG(1) << "Updating previously managed configuration with the " 173 << "updated policy " << new_guid << "."; 174 } else { 175 DeleteEntry(entry); 176 } 177 178 const base::DictionaryValue* user_settings = 179 ui_data ? ui_data->user_settings() : NULL; 180 181 // Write the new configuration. 182 CreateAndWriteNewShillConfiguration(new_guid, *new_policy, user_settings); 183 remaining_policies_.erase(new_guid); 184 } 185 } else if (was_managed) { 186 VLOG(1) << "Removing configuration previously managed by policy " 187 << old_guid << ", because the policy was removed."; 188 189 // Remove the entry, because the network was managed but isn't anymore. 190 // Note: An alternative might be to preserve the user settings, but it's 191 // unclear which values originating the policy should be removed. 192 DeleteEntry(entry); 193 } else { 194 VLOG(2) << "Ignore unmanaged entry."; 195 196 // The entry wasn't managed and doesn't match any current policy. Thus 197 // leave it as it is. 198 } 199} 200 201void PolicyApplicator::DeleteEntry(const std::string& entry) { 202 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( 203 dbus::ObjectPath(profile_.path), 204 entry, 205 base::Bind(&base::DoNothing), 206 base::Bind(&LogErrorMessage, FROM_HERE)); 207} 208 209void PolicyApplicator::CreateAndWriteNewShillConfiguration( 210 const std::string& guid, 211 const base::DictionaryValue& policy, 212 const base::DictionaryValue* user_settings) { 213 // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per 214 // user. Abort in that case. 215 std::string type; 216 policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type); 217 if (type == ::onc::network_type::kEthernet && 218 profile_.type() == NetworkProfile::TYPE_USER) { 219 const base::DictionaryValue* ethernet = NULL; 220 policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet, 221 ðernet); 222 std::string auth; 223 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 224 &auth); 225 if (auth == ::onc::ethernet::kNone) 226 return; 227 } 228 229 scoped_ptr<base::DictionaryValue> shill_dictionary = 230 policy_util::CreateShillConfiguration( 231 profile_, guid, &policy, user_settings); 232 handler_->CreateConfigurationFromPolicy(*shill_dictionary); 233} 234 235PolicyApplicator::~PolicyApplicator() { 236 ApplyRemainingPolicies(); 237 STLDeleteValues(&all_policies_); 238} 239 240void PolicyApplicator::ApplyRemainingPolicies() { 241 if (!handler_) { 242 LOG(WARNING) << "Handler destructed during policy application to profile " 243 << profile_.ToDebugString(); 244 return; 245 } 246 247 if (remaining_policies_.empty()) 248 return; 249 250 VLOG(2) << "Create new managed network configurations in profile" 251 << profile_.ToDebugString() << "."; 252 // All profile entries were compared to policies. |remaining_policies_| 253 // contains all modified policies that didn't match any entry. For these 254 // remaining policies, new configurations have to be created. 255 for (std::set<std::string>::iterator it = remaining_policies_.begin(); 256 it != remaining_policies_.end(); ++it) { 257 const base::DictionaryValue* policy = GetByGUID(all_policies_, *it); 258 DCHECK(policy); 259 260 VLOG(1) << "Creating new configuration managed by policy " << *it 261 << " in profile " << profile_.ToDebugString() << "."; 262 263 CreateAndWriteNewShillConfiguration( 264 *it, *policy, NULL /* no user settings */); 265 } 266} 267 268} // namespace chromeos 269