blob: 5ee791fb3b24db80dc832ed9307158579f071dcf [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/network/managed_network_configuration_handler.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/guid.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/dbus/dbus_method_call_status.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_manager_client.h"
#include "chromeos/dbus/shill_profile_client.h"
#include "chromeos/dbus/shill_service_client.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_handler_callbacks.h"
#include "chromeos/network/network_profile.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_ui_data.h"
#include "chromeos/network/onc/onc_constants.h"
#include "chromeos/network/onc/onc_merger.h"
#include "chromeos/network/onc/onc_normalizer.h"
#include "chromeos/network/onc/onc_signature.h"
#include "chromeos/network/onc/onc_translator.h"
#include "chromeos/network/onc/onc_utils.h"
#include "chromeos/network/onc/onc_validator.h"
#include "dbus/object_path.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
// These are error strings used for error callbacks. None of these error
// messages are user-facing: they should only appear in logs.
const char kInvalidUserSettingsMessage[] = "User settings are invalid.";
const char kInvalidUserSettings[] = "Error.InvalidUserSettings";
const char kNetworkAlreadyConfiguredMessage[] =
"Network is already configured.";
const char kNetworkAlreadyConfigured[] = "Error.NetworkAlreadyConfigured";
const char kPoliciesNotInitializedMessage[] = "Policies not initialized.";
const char kPoliciesNotInitialized[] = "Error.PoliciesNotInitialized";
const char kProfileNotInitializedMessage[] = "Profile not initialized.";
const char kProfileNotInitialized[] = "Error.ProflieNotInitialized";
const char kSetOnUnconfiguredNetworkMessage[] =
"Unable to modify properties of an unconfigured network.";
const char kSetOnUnconfiguredNetwork[] = "Error.SetCalledOnUnconfiguredNetwork";
const char kUnknownProfilePathMessage[] = "Profile path is unknown.";
const char kUnknownProfilePath[] = "Error.UnknownProfilePath";
const char kUnknownServicePathMessage[] = "Service path is unknown.";
const char kUnknownServicePath[] = "Error.UnknownServicePath";
// This fake credential contains a random postfix which is extremly unlikely to
// be used by any user.
const char kFakeCredential[] = "FAKE_CREDENTIAL_VPaJDV9x";
std::string ToDebugString(onc::ONCSource source,
const std::string& userhash) {
return source == onc::ONC_SOURCE_USER_POLICY ?
("user policy of " + userhash) : "device policy";
}
void RunErrorCallback(const std::string& service_path,
const std::string& error_name,
const std::string& error_message,
const network_handler::ErrorCallback& error_callback) {
NET_LOG_ERROR(error_name, error_message);
error_callback.Run(
error_name,
make_scoped_ptr(
network_handler::CreateErrorData(service_path,
error_name,
error_message)));
}
// Sets the UIData property in |shill_dictionary| to the serialization of
// |ui_data|.
void SetUIData(const NetworkUIData& ui_data,
base::DictionaryValue* shill_dictionary) {
base::DictionaryValue ui_data_dict;
ui_data.FillDictionary(&ui_data_dict);
std::string ui_data_blob;
base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
shill_dictionary->SetStringWithoutPathExpansion(flimflam::kUIDataProperty,
ui_data_blob);
}
// A dummy callback to ignore the result of Shill calls.
void IgnoreString(const std::string& str) {
}
void LogErrorWithDict(const tracked_objects::Location& from_where,
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
LOG(ERROR) << from_where.ToString() << ": " << error_name;
}
void LogErrorMessage(const tracked_objects::Location& from_where,
const std::string& error_name,
const std::string& error_message) {
LOG(ERROR) << from_where.ToString() << ": " << error_message;
}
// Removes all kFakeCredential values from sensitive fields (determined by
// onc::FieldIsCredential) of |onc_object|.
void RemoveFakeCredentials(
const onc::OncValueSignature& signature,
base::DictionaryValue* onc_object) {
base::DictionaryValue::Iterator it(*onc_object);
while (!it.IsAtEnd()) {
base::Value* value = NULL;
std::string field_name = it.key();
// We need the non-const entry to remove nested values but DictionaryValue
// has no non-const iterator.
onc_object->GetWithoutPathExpansion(field_name, &value);
// Advance before delete.
it.Advance();
// If |value| is a dictionary, recurse.
base::DictionaryValue* nested_object = NULL;
if (value->GetAsDictionary(&nested_object)) {
const onc::OncFieldSignature* field_signature =
onc::GetFieldSignature(signature, field_name);
RemoveFakeCredentials(*field_signature->value_signature,
nested_object);
continue;
}
// If |value| is a string, check if it is a fake credential.
std::string string_value;
if (value->GetAsString(&string_value) &&
onc::FieldIsCredential(signature, field_name)) {
if (string_value == kFakeCredential) {
// The value wasn't modified by the UI, thus we remove the field to keep
// the existing value that is stored in Shill.
onc_object->RemoveWithoutPathExpansion(field_name, NULL);
}
// Otherwise, the value is set and modified by the UI, thus we keep that
// value to overwrite whatever is stored in Shill.
}
}
}
// Creates a Shill property dictionary from the given arguments. The resulting
// dictionary will be sent to Shill by the caller. Depending on the profile
// type, |policy| is interpreted as the user or device policy and |settings| as
// the user or shared settings.
scoped_ptr<base::DictionaryValue> CreateShillConfiguration(
const NetworkProfile& profile,
const std::string& guid,
const base::DictionaryValue* policy,
const base::DictionaryValue* settings) {
scoped_ptr<base::DictionaryValue> effective;
onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
if (policy) {
if (profile.type() == NetworkProfile::TYPE_SHARED) {
effective = onc::MergeSettingsAndPoliciesToEffective(
NULL, // no user policy
policy, // device policy
NULL, // no user settings
settings); // shared settings
onc_source = onc::ONC_SOURCE_DEVICE_POLICY;
} else if (profile.type() == NetworkProfile::TYPE_USER) {
effective = onc::MergeSettingsAndPoliciesToEffective(
policy, // user policy
NULL, // no device policy
settings, // user settings
NULL); // no shared settings
onc_source = onc::ONC_SOURCE_USER_POLICY;
} else {
NOTREACHED();
}
} else if (settings) {
effective.reset(settings->DeepCopy());
// TODO(pneubeck): change to source ONC_SOURCE_USER
onc_source = onc::ONC_SOURCE_NONE;
} else {
NOTREACHED();
onc_source = onc::ONC_SOURCE_NONE;
}
RemoveFakeCredentials(onc::kNetworkConfigurationSignature,
effective.get());
effective->SetStringWithoutPathExpansion(onc::network_config::kGUID, guid);
// Remove irrelevant fields.
onc::Normalizer normalizer(true /* remove recommended fields */);
effective = normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
*effective);
scoped_ptr<base::DictionaryValue> shill_dictionary(
onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
*effective));
shill_dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty,
profile.path);
scoped_ptr<NetworkUIData> ui_data;
if (policy)
ui_data = NetworkUIData::CreateFromONC(onc_source, *policy);
else
ui_data.reset(new NetworkUIData());
if (settings) {
// Shill doesn't know that sensitive data is contained in the UIData
// property and might write it into logs or other insecure places. Thus, we
// have to remove or mask credentials.
//
// Shill's GetProperties doesn't return credentials. Masking credentials
// instead of just removing them, allows remembering if a credential is set
// or not.
scoped_ptr<base::DictionaryValue> sanitized_settings(
onc::MaskCredentialsInOncObject(onc::kNetworkConfigurationSignature,
*settings,
kFakeCredential));
ui_data->set_user_settings(sanitized_settings.Pass());
}
SetUIData(*ui_data, shill_dictionary.get());
VLOG(2) << "Created Shill properties: " << *shill_dictionary;
return shill_dictionary.Pass();
}
// Returns true if |policy| matches |actual_network|, which must be part of a
// ONC NetworkConfiguration. This should be the only such matching function
// within Chrome. Shill does such matching in several functions for network
// identification. For compatibility, we currently should stick to Shill's
// matching behavior.
bool IsPolicyMatching(const base::DictionaryValue& policy,
const base::DictionaryValue& actual_network) {
std::string policy_type;
policy.GetStringWithoutPathExpansion(onc::network_config::kType,
&policy_type);
std::string network_type;
actual_network.GetStringWithoutPathExpansion(onc::network_config::kType,
&network_type);
if (policy_type != network_type)
return false;
if (network_type != onc::network_type::kWiFi)
return false;
const base::DictionaryValue* policy_wifi = NULL;
policy.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi,
&policy_wifi);
const base::DictionaryValue* actual_wifi = NULL;
actual_network.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi,
&actual_wifi);
if (!policy_wifi || !actual_wifi)
return false;
std::string policy_ssid;
policy_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &policy_ssid);
std::string actual_ssid;
actual_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &actual_ssid);
return (policy_ssid == actual_ssid);
}
// Returns the policy from |policies| matching |actual_network|, if any exists.
// Returns NULL otherwise. |actual_network| must be part of a ONC
// NetworkConfiguration.
const base::DictionaryValue* FindMatchingPolicy(
const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies,
const base::DictionaryValue& actual_network) {
for (ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it =
policies.begin(); it != policies.end(); ++it) {
if (IsPolicyMatching(*it->second, actual_network))
return it->second;
}
return NULL;
}
const base::DictionaryValue* GetByGUID(
const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies,
const std::string& guid) {
ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it =
policies.find(guid);
if (it == policies.end())
return NULL;
return it->second;
}
void TranslatePropertiesToOncAndRunCallback(
const network_handler::DictionaryResultCallback& callback,
const std::string& service_path,
const base::DictionaryValue& shill_properties) {
scoped_ptr<base::DictionaryValue> onc_network(
onc::TranslateShillServiceToONCPart(
shill_properties,
&onc::kNetworkWithStateSignature));
callback.Run(service_path, *onc_network);
}
} // namespace
// This class compares (entry point is Run()) |modified_policies| with the
// existing entries in the provided Shill profile |profile|. It fetches all
// entries in parallel (GetProfilePropertiesCallback), compares each entry with
// the current policies (GetEntryCallback) and adds all missing policies
// (~PolicyApplicator).
class ManagedNetworkConfigurationHandler::PolicyApplicator
: public base::RefCounted<PolicyApplicator> {
public:
typedef ManagedNetworkConfigurationHandler::GuidToPolicyMap GuidToPolicyMap;
// |modified_policies| must not be NULL and will be empty afterwards.
PolicyApplicator(base::WeakPtr<ManagedNetworkConfigurationHandler> handler,
const NetworkProfile& profile,
std::set<std::string>* modified_policies);
void Run();
private:
friend class base::RefCounted<PolicyApplicator>;
// Called with the properties of the profile |profile_|. Requests the
// properties of each entry, which are processed by GetEntryCallback.
void GetProfilePropertiesCallback(
const base::DictionaryValue& profile_properties);
// Called with the properties of the profile entry |entry|. Checks whether the
// entry was previously managed, whether a current policy applies and then
// either updates, deletes or not touches the entry.
void GetEntryCallback(const std::string& entry,
const base::DictionaryValue& entry_properties);
// Sends Shill the command to delete profile entry |entry| from |profile_|.
void DeleteEntry(const std::string& entry);
// Creates new entries for all remaining policies, i.e. for which not matching
// entry was found.
virtual ~PolicyApplicator();
std::set<std::string> remaining_policies_;
base::WeakPtr<ManagedNetworkConfigurationHandler> handler_;
NetworkProfile profile_;
DISALLOW_COPY_AND_ASSIGN(PolicyApplicator);
};
// static
scoped_ptr<NetworkUIData> ManagedNetworkConfigurationHandler::GetUIData(
const base::DictionaryValue& shill_dictionary) {
std::string ui_data_blob;
if (shill_dictionary.GetStringWithoutPathExpansion(
flimflam::kUIDataProperty,
&ui_data_blob) &&
!ui_data_blob.empty()) {
scoped_ptr<base::DictionaryValue> ui_data_dict =
onc::ReadDictionaryFromJson(ui_data_blob);
if (ui_data_dict)
return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
else
LOG(ERROR) << "UIData is not a valid JSON dictionary.";
}
VLOG(2) << "JSON dictionary has no UIData blob: " << shill_dictionary;
return scoped_ptr<NetworkUIData>();
}
void ManagedNetworkConfigurationHandler::GetManagedProperties(
const std::string& userhash,
const std::string& service_path,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback) {
if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) {
RunErrorCallback(service_path,
kPoliciesNotInitialized,
kPoliciesNotInitializedMessage,
error_callback);
return;
}
network_configuration_handler_->GetProperties(
service_path,
base::Bind(
&ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
error_callback),
error_callback);
}
void ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback(
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback,
const std::string& service_path,
const base::DictionaryValue& shill_properties) {
std::string profile_path;
shill_properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty,
&profile_path);
LOG(ERROR) << "Profile: " << profile_path;
const NetworkProfile* profile =
network_profile_handler_->GetProfileForPath(profile_path);
if (!profile) {
LOG(ERROR) << "No or no known profile received for service "
<< service_path << ".";
}
scoped_ptr<NetworkUIData> ui_data = GetUIData(shill_properties);
const base::DictionaryValue* user_settings = NULL;
const base::DictionaryValue* shared_settings = NULL;
if (ui_data && profile) {
if (profile->type() == NetworkProfile::TYPE_SHARED)
shared_settings = ui_data->user_settings();
else if (profile->type() == NetworkProfile::TYPE_USER)
user_settings = ui_data->user_settings();
else
NOTREACHED();
} else if (profile) {
LOG(WARNING) << "Service " << service_path << " of profile "
<< profile_path << " contains no or no valid UIData.";
// TODO(pneubeck): add a conversion of user configured entries of old
// ChromeOS versions. We will have to use a heuristic to determine which
// properties _might_ be user configured.
}
scoped_ptr<base::DictionaryValue> active_settings(
onc::TranslateShillServiceToONCPart(
shill_properties,
&onc::kNetworkWithStateSignature));
std::string guid;
active_settings->GetStringWithoutPathExpansion(onc::network_config::kGUID,
&guid);
const base::DictionaryValue* user_policy = NULL;
const base::DictionaryValue* device_policy = NULL;
if (!guid.empty() && profile) {
const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile);
if (!policies) {
RunErrorCallback(service_path,
kPoliciesNotInitialized,
kPoliciesNotInitializedMessage,
error_callback);
return;
}
const base::DictionaryValue* policy = GetByGUID(*policies, guid);
if (profile->type() == NetworkProfile::TYPE_SHARED)
device_policy = policy;
else if (profile->type() == NetworkProfile::TYPE_USER)
user_policy = policy;
else
NOTREACHED();
}
// This call also removes credentials from policies.
scoped_ptr<base::DictionaryValue> augmented_properties =
onc::MergeSettingsAndPoliciesToAugmented(
onc::kNetworkConfigurationSignature,
user_policy,
device_policy,
user_settings,
shared_settings,
active_settings.get());
callback.Run(service_path, *augmented_properties);
}
void ManagedNetworkConfigurationHandler::GetProperties(
const std::string& service_path,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback) const {
network_configuration_handler_->GetProperties(
service_path,
base::Bind(&TranslatePropertiesToOncAndRunCallback, callback),
error_callback);
}
void ManagedNetworkConfigurationHandler::SetProperties(
const std::string& service_path,
const base::DictionaryValue& user_settings,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) const {
const NetworkState* state =
network_state_handler_->GetNetworkState(service_path);
if (!state) {
RunErrorCallback(service_path,
kUnknownServicePath,
kUnknownServicePathMessage,
error_callback);
return;
}
std::string guid = state->guid();
if (guid.empty()) {
// TODO(pneubeck): create an initial configuration in this case. As for
// CreateConfiguration, user settings from older ChromeOS versions have to
// determined here.
RunErrorCallback(service_path,
kSetOnUnconfiguredNetwork,
kSetOnUnconfiguredNetworkMessage,
error_callback);
return;
}
const std::string& profile_path = state->profile_path();
const NetworkProfile *profile =
network_profile_handler_->GetProfileForPath(profile_path);
if (!profile) {
RunErrorCallback(service_path,
kUnknownProfilePath,
kUnknownProfilePathMessage,
error_callback);
return;
}
VLOG(2) << "SetProperties: Found GUID " << guid << " and profile "
<< profile->ToDebugString();
const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile);
if (!policies) {
RunErrorCallback(service_path,
kPoliciesNotInitialized,
kPoliciesNotInitializedMessage,
error_callback);
return;
}
// Validate the ONC dictionary. We are liberal and ignore unknown field
// names. User settings are only partial ONC, thus we ignore missing fields.
onc::Validator validator(false, // Ignore unknown fields.
false, // Ignore invalid recommended field names.
false, // Ignore missing fields.
false); // This ONC does not come from policy.
onc::Validator::Result validation_result;
scoped_ptr<base::DictionaryValue> validated_user_settings =
validator.ValidateAndRepairObject(
&onc::kNetworkConfigurationSignature,
user_settings,
&validation_result);
if (validation_result == onc::Validator::INVALID) {
RunErrorCallback(service_path,
kInvalidUserSettings,
kInvalidUserSettingsMessage,
error_callback);
return;
}
if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
LOG(WARNING) << "Validation of ONC user settings produced warnings.";
const base::DictionaryValue* policy = GetByGUID(*policies, guid);
VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed.";
scoped_ptr<base::DictionaryValue> shill_dictionary(CreateShillConfiguration(
*profile, guid, policy, validated_user_settings.get()));
network_configuration_handler_->SetProperties(
service_path, *shill_dictionary, callback, error_callback);
}
void ManagedNetworkConfigurationHandler::CreateConfiguration(
const std::string& userhash,
const base::DictionaryValue& properties,
const network_handler::StringResultCallback& callback,
const network_handler::ErrorCallback& error_callback) const {
const GuidToPolicyMap* policies = GetPoliciesForUser(userhash);
if (!policies) {
RunErrorCallback("",
kPoliciesNotInitialized,
kPoliciesNotInitializedMessage,
error_callback);
return;
}
if (FindMatchingPolicy(*policies, properties)) {
RunErrorCallback("",
kNetworkAlreadyConfigured,
kNetworkAlreadyConfiguredMessage,
error_callback);
}
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
RunErrorCallback("",
kProfileNotInitialized,
kProfileNotInitializedMessage,
error_callback);
}
// TODO(pneubeck): In case of WiFi, check that no other configuration for the
// same {SSID, mode, security} exists. We don't support such multiple
// configurations, yet.
// Generate a new GUID for this configuration. Ignore the maybe provided GUID
// in |properties| as it is not our own and from an untrusted source.
std::string guid = base::GenerateGUID();
scoped_ptr<base::DictionaryValue> shill_dictionary(
CreateShillConfiguration(*profile, guid, NULL /*no policy*/,
&properties));
network_configuration_handler_->CreateConfiguration(
*shill_dictionary, callback, error_callback);
}
void ManagedNetworkConfigurationHandler::RemoveConfiguration(
const std::string& service_path,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) const {
network_configuration_handler_->RemoveConfiguration(
service_path, callback, error_callback);
}
void ManagedNetworkConfigurationHandler::SetPolicy(
onc::ONCSource onc_source,
const std::string& userhash,
const base::ListValue& network_configs_onc) {
VLOG(1) << "Setting policies from " << ToDebugString(onc_source, userhash)
<< ".";
// |userhash| must be empty for device policies.
DCHECK(onc_source != chromeos::onc::ONC_SOURCE_DEVICE_POLICY ||
userhash.empty());
GuidToPolicyMap& policies = policies_by_user_[userhash];
GuidToPolicyMap old_policies;
policies.swap(old_policies);
// This stores all GUIDs of policies that have changed or are new.
std::set<std::string> modified_policies;
for (base::ListValue::const_iterator it = network_configs_onc.begin();
it != network_configs_onc.end(); ++it) {
const base::DictionaryValue* network = NULL;
(*it)->GetAsDictionary(&network);
DCHECK(network);
std::string guid;
network->GetStringWithoutPathExpansion(onc::network_config::kGUID, &guid);
DCHECK(!guid.empty());
if (policies.count(guid) > 0) {
LOG(ERROR) << "ONC from " << ToDebugString(onc_source, userhash)
<< " contains several entries for the same GUID "
<< guid << ".";
delete policies[guid];
}
const base::DictionaryValue* new_entry = network->DeepCopy();
policies[guid] = new_entry;
const base::DictionaryValue* old_entry = old_policies[guid];
if (!old_entry || !old_entry->Equals(new_entry))
modified_policies.insert(guid);
}
STLDeleteValues(&old_policies);
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
VLOG(1) << "The relevant Shill profile isn't initialized yet, postponing "
<< "policy application.";
return;
}
scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
weak_ptr_factory_.GetWeakPtr(),
*profile,
&modified_policies);
applicator->Run();
}
void ManagedNetworkConfigurationHandler::OnProfileAdded(
const NetworkProfile& profile) {
VLOG(1) << "Adding profile " << profile.ToDebugString() << "'.";
const GuidToPolicyMap* policies = GetPoliciesForProfile(profile);
if (!policies) {
VLOG(1) << "The relevant policy is not initialized, "
<< "postponing policy application.";
return;
}
std::set<std::string> policy_guids;
for (GuidToPolicyMap::const_iterator it = policies->begin();
it != policies->end(); ++it) {
policy_guids.insert(it->first);
}
scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
weak_ptr_factory_.GetWeakPtr(),
profile,
&policy_guids);
applicator->Run();
}
const base::DictionaryValue*
ManagedNetworkConfigurationHandler::FindPolicyByGUID(
const std::string userhash,
const std::string& guid,
onc::ONCSource* onc_source) const {
*onc_source = onc::ONC_SOURCE_NONE;
if (!userhash.empty()) {
const GuidToPolicyMap* user_policies = GetPoliciesForUser(userhash);
if (user_policies) {
GuidToPolicyMap::const_iterator found = user_policies->find(guid);
if (found != user_policies->end()) {
*onc_source = onc::ONC_SOURCE_USER_POLICY;
return found->second;
}
}
}
const GuidToPolicyMap* device_policies = GetPoliciesForUser(std::string());
if (device_policies) {
GuidToPolicyMap::const_iterator found = device_policies->find(guid);
if (found != device_policies->end()) {
*onc_source = onc::ONC_SOURCE_DEVICE_POLICY;
return found->second;
}
}
return NULL;
}
void ManagedNetworkConfigurationHandler::OnProfileRemoved(
const NetworkProfile& profile) {
// Nothing to do in this case.
}
const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
ManagedNetworkConfigurationHandler::GetPoliciesForUser(
const std::string& userhash) const {
UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash);
if (it == policies_by_user_.end())
return NULL;
return &it->second;
}
const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
ManagedNetworkConfigurationHandler::GetPoliciesForProfile(
const NetworkProfile& profile) const {
DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
profile.userhash.empty());
return GetPoliciesForUser(profile.userhash);
}
ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler()
: network_state_handler_(NULL),
network_profile_handler_(NULL),
network_configuration_handler_(NULL),
weak_ptr_factory_(this) {
}
ManagedNetworkConfigurationHandler::~ManagedNetworkConfigurationHandler() {
network_profile_handler_->RemoveObserver(this);
for (UserToPoliciesMap::iterator it = policies_by_user_.begin();
it != policies_by_user_.end(); ++it) {
STLDeleteValues(&it->second);
}
}
void ManagedNetworkConfigurationHandler::Init(
NetworkStateHandler* network_state_handler,
NetworkProfileHandler* network_profile_handler,
NetworkConfigurationHandler* network_configuration_handler) {
network_state_handler_ = network_state_handler;
network_profile_handler_ = network_profile_handler;
network_configuration_handler_ = network_configuration_handler;
network_profile_handler_->AddObserver(this);
}
ManagedNetworkConfigurationHandler::PolicyApplicator::PolicyApplicator(
base::WeakPtr<ManagedNetworkConfigurationHandler> handler,
const NetworkProfile& profile,
std::set<std::string>* modified_policies)
: handler_(handler), profile_(profile) {
remaining_policies_.swap(*modified_policies);
}
void ManagedNetworkConfigurationHandler::PolicyApplicator::Run() {
DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
dbus::ObjectPath(profile_.path),
base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
base::Bind(&LogErrorMessage, FROM_HERE));
}
void ManagedNetworkConfigurationHandler::PolicyApplicator::
GetProfilePropertiesCallback(
const base::DictionaryValue& profile_properties) {
if (!handler_) {
LOG(WARNING) << "Handler destructed during policy application to profile "
<< profile_.ToDebugString();
return;
}
VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
const base::ListValue* entries = NULL;
if (!profile_properties.GetListWithoutPathExpansion(
flimflam::kEntriesProperty, &entries)) {
LOG(ERROR) << "Profile " << profile_.ToDebugString()
<< " doesn't contain the property "
<< flimflam::kEntriesProperty;
return;
}
for (base::ListValue::const_iterator it = entries->begin();
it != entries->end();
++it) {
std::string entry;
(*it)->GetAsString(&entry);
std::ostringstream entry_failure;
DBusThreadManager::Get()->GetShillProfileClient()
->GetEntry(dbus::ObjectPath(profile_.path),
entry,
base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
base::Bind(&LogErrorMessage, FROM_HERE));
}
}
void ManagedNetworkConfigurationHandler::PolicyApplicator::GetEntryCallback(
const std::string& entry,
const base::DictionaryValue& entry_properties) {
if (!handler_) {
LOG(WARNING) << "Handler destructed during policy application to profile "
<< profile_.ToDebugString();
return;
}
VLOG(2) << "Received properties for entry " << entry << " of profile "
<< profile_.ToDebugString();
scoped_ptr<base::DictionaryValue> onc_part(
onc::TranslateShillServiceToONCPart(entry_properties,
&onc::kNetworkWithStateSignature));
std::string old_guid;
if (!onc_part->GetStringWithoutPathExpansion(onc::network_config::kGUID,
&old_guid)) {
VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
<< " doesn't contain a GUID.";
// This might be an entry of an older ChromeOS version. Assume it to be
// unmanaged.
}
scoped_ptr<NetworkUIData> ui_data = GetUIData(entry_properties);
if (!ui_data) {
VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
<< " contains no or no valid UIData.";
// This might be an entry of an older ChromeOS version. Assume it to be
// unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
// clear the GUID just in case.
old_guid.clear();
}
bool was_managed = !old_guid.empty() && ui_data &&
(ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY ||
ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY);
// The relevant policy must have been initialized, otherwise we hadn't Run
// this PolicyApplicator.
const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_);
const base::DictionaryValue* new_policy = NULL;
if (was_managed) {
// If we have a GUID that might match a current policy, do a lookup using
// that GUID at first. In particular this is necessary, as some networks
// can't be matched to policies by properties (e.g. VPN).
new_policy = GetByGUID(policies, old_guid);
}
if (!new_policy) {
// If we didn't find a policy by GUID, still a new policy might match.
new_policy = FindMatchingPolicy(policies, *onc_part);
}
if (new_policy) {
std::string new_guid;
new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID,
&new_guid);
VLOG_IF(1, was_managed && old_guid != new_guid)
<< "Updating configuration previously managed by policy " << old_guid
<< " with new policy " << new_guid << ".";
VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
<< " to previously unmanaged "
<< "configuration.";
if (old_guid == new_guid &&
remaining_policies_.find(new_guid) == remaining_policies_.end()) {
VLOG(1) << "Not updating existing managed configuration with guid "
<< new_guid << " because the policy didn't change.";
} else {
// Delete the entry to ensure that no old configuration remains.
// Don't do this if a policy is reapplied (e.g. after reboot) or updated
// (i.e. the GUID didn't change), in order to keep implicit state of
// Shill like "connected successfully before".
if (old_guid == new_guid) {
VLOG(1) << "Updating previously managed configuration with the "
<< "updated policy " << new_guid << ".";
} else {
DeleteEntry(entry);
}
const base::DictionaryValue* user_settings =
ui_data ? ui_data->user_settings() : NULL;
// Write the new configuration.
scoped_ptr<base::DictionaryValue> shill_dictionary =
CreateShillConfiguration(
profile_, new_guid, new_policy, user_settings);
handler_->network_configuration_handler()
->CreateConfiguration(*shill_dictionary,
base::Bind(&IgnoreString),
base::Bind(&LogErrorWithDict, FROM_HERE));
remaining_policies_.erase(new_guid);
}
} else if (was_managed) {
VLOG(1) << "Removing configuration previously managed by policy "
<< old_guid << ", because the policy was removed.";
// Remove the entry, because the network was managed but isn't anymore.
// Note: An alternative might be to preserve the user settings, but it's
// unclear which values originating the policy should be removed.
DeleteEntry(entry);
} else {
VLOG(2) << "Ignore unmanaged entry.";
// The entry wasn't managed and doesn't match any current policy. Thus
// leave it as it is.
}
}
void ManagedNetworkConfigurationHandler::PolicyApplicator::DeleteEntry(
const std::string& entry) {
DBusThreadManager::Get()->GetShillProfileClient()
->DeleteEntry(dbus::ObjectPath(profile_.path),
entry,
base::Bind(&base::DoNothing),
base::Bind(&LogErrorMessage, FROM_HERE));
}
ManagedNetworkConfigurationHandler::PolicyApplicator::~PolicyApplicator() {
if (!handler_) {
LOG(WARNING) << "Handler destructed during policy application to profile "
<< profile_.ToDebugString();
return;
}
if (remaining_policies_.empty())
return;
VLOG(2) << "Create new managed network configurations in profile"
<< profile_.ToDebugString() << ".";
// All profile entries were compared to policies. |configureGUIDs_| contains
// all matched policies. From the remainder of policies, new configurations
// have to be created.
// The relevant policy must have been initialized, otherwise we hadn't Run
// this PolicyApplicator.
const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_);
for (std::set<std::string>::iterator it = remaining_policies_.begin();
it != remaining_policies_.end();
++it) {
const base::DictionaryValue* policy = GetByGUID(policies, *it);
if (!policy) {
LOG(ERROR) << "Policy " << *it << " doesn't exist anymore.";
continue;
}
VLOG(1) << "Creating new configuration managed by policy " << *it
<< " in profile " << profile_.ToDebugString() << ".";
scoped_ptr<base::DictionaryValue> shill_dictionary =
CreateShillConfiguration(
profile_, *it, policy, NULL /* no user settings */);
handler_->network_configuration_handler()
->CreateConfiguration(*shill_dictionary,
base::Bind(&IgnoreString),
base::Bind(&LogErrorWithDict, FROM_HERE));
}
}
} // namespace chromeos