policy_applicator.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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_constants.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 "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 flimflam::kEntriesProperty, &entries)) { 77 LOG(ERROR) << "Profile " << profile_.ToDebugString() 78 << " doesn't contain the property " 79 << flimflam::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() == onc::ONC_SOURCE_DEVICE_POLICY || 134 ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY); 135 136 const base::DictionaryValue* new_policy = NULL; 137 if (was_managed) { 138 // If we have a GUID that might match a current policy, do a lookup using 139 // that GUID at first. In particular this is necessary, as some networks 140 // can't be matched to policies by properties (e.g. VPN). 141 new_policy = GetByGUID(all_policies_, old_guid); 142 } 143 144 if (!new_policy) { 145 // If we didn't find a policy by GUID, still a new policy might match. 146 new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part); 147 } 148 149 if (new_policy) { 150 std::string new_guid; 151 new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID, 152 &new_guid); 153 154 VLOG_IF(1, was_managed && old_guid != new_guid) 155 << "Updating configuration previously managed by policy " << old_guid 156 << " with new policy " << new_guid << "."; 157 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid 158 << " to previously unmanaged " 159 << "configuration."; 160 161 if (old_guid == new_guid && 162 remaining_policies_.find(new_guid) == remaining_policies_.end()) { 163 VLOG(1) << "Not updating existing managed configuration with guid " 164 << new_guid << " because the policy didn't change."; 165 } else { 166 // Delete the entry to ensure that no old configuration remains. 167 // Don't do this if a policy is reapplied (e.g. after reboot) or updated 168 // (i.e. the GUID didn't change), in order to keep implicit state of 169 // Shill like "connected successfully before". 170 if (old_guid == new_guid) { 171 VLOG(1) << "Updating previously managed configuration with the " 172 << "updated policy " << new_guid << "."; 173 } else { 174 DeleteEntry(entry); 175 } 176 177 const base::DictionaryValue* user_settings = 178 ui_data ? ui_data->user_settings() : NULL; 179 180 // Write the new configuration. 181 CreateAndWriteNewShillConfiguration(new_guid, *new_policy, user_settings); 182 remaining_policies_.erase(new_guid); 183 } 184 } else if (was_managed) { 185 VLOG(1) << "Removing configuration previously managed by policy " 186 << old_guid << ", because the policy was removed."; 187 188 // Remove the entry, because the network was managed but isn't anymore. 189 // Note: An alternative might be to preserve the user settings, but it's 190 // unclear which values originating the policy should be removed. 191 DeleteEntry(entry); 192 } else { 193 VLOG(2) << "Ignore unmanaged entry."; 194 195 // The entry wasn't managed and doesn't match any current policy. Thus 196 // leave it as it is. 197 } 198} 199 200void PolicyApplicator::DeleteEntry(const std::string& entry) { 201 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( 202 dbus::ObjectPath(profile_.path), 203 entry, 204 base::Bind(&base::DoNothing), 205 base::Bind(&LogErrorMessage, FROM_HERE)); 206} 207 208void PolicyApplicator::CreateAndWriteNewShillConfiguration( 209 const std::string& guid, 210 const base::DictionaryValue& policy, 211 const base::DictionaryValue* user_settings) { 212 scoped_ptr<base::DictionaryValue> shill_dictionary = 213 policy_util::CreateShillConfiguration( 214 profile_, guid, &policy, user_settings); 215 handler_->CreateConfigurationFromPolicy(*shill_dictionary); 216} 217 218PolicyApplicator::~PolicyApplicator() { 219 ApplyRemainingPolicies(); 220 STLDeleteValues(&all_policies_); 221} 222 223void PolicyApplicator::ApplyRemainingPolicies() { 224 if (!handler_) { 225 LOG(WARNING) << "Handler destructed during policy application to profile " 226 << profile_.ToDebugString(); 227 return; 228 } 229 230 if (remaining_policies_.empty()) 231 return; 232 233 VLOG(2) << "Create new managed network configurations in profile" 234 << profile_.ToDebugString() << "."; 235 // All profile entries were compared to policies. |remaining_policies_| 236 // contains all modified policies that didn't match any entry. For these 237 // remaining policies, new configurations have to be created. 238 for (std::set<std::string>::iterator it = remaining_policies_.begin(); 239 it != remaining_policies_.end(); ++it) { 240 const base::DictionaryValue* policy = GetByGUID(all_policies_, *it); 241 DCHECK(policy); 242 243 VLOG(1) << "Creating new configuration managed by policy " << *it 244 << " in profile " << profile_.ToDebugString() << "."; 245 246 CreateAndWriteNewShillConfiguration( 247 *it, *policy, NULL /* no user settings */); 248 } 249} 250 251} // namespace chromeos 252