This source file includes following definitions.
- InvokeErrorCallback
- IsAuthenticationError
- VPNRequiresCredentials
- GetDefaultUserProfilePath
- certificates_loaded_
- Init
- LoggedInStateChanged
- OnCertificatesLoaded
- ConnectToNetwork
- DisconnectNetwork
- HasConnectingNetwork
- HasPendingConnectRequest
- NetworkListChanged
- NetworkPropertiesUpdated
- GetPendingRequest
- VerifyConfiguredAndConnect
- QueueConnectRequest
- CheckCertificatesLoaded
- ConnectToQueuedNetwork
- CallShillConnect
- HandleConfigurationFailure
- HandleShillConnectSuccess
- HandleShillConnectFailure
- CheckPendingRequest
- CheckAllPendingRequests
- CertificateIsConfigured
- ErrorCallbackForPendingRequest
- CallShillDisconnect
- HandleShillDisconnectSuccess
#include "chromeos/network/network_connection_handler.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/cert_loader.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/shill_manager_client.h"
#include "chromeos/dbus/shill_service_client.h"
#include "chromeos/network/client_cert_util.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_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/shill_property_util.h"
#include "chromeos/tpm_token_loader.h"
#include "dbus/object_path.h"
#include "net/cert/x509_certificate.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
void InvokeErrorCallback(const std::string& service_path,
const network_handler::ErrorCallback& error_callback,
const std::string& error_name) {
NET_LOG_ERROR("Connect Error: " + error_name, service_path);
network_handler::RunErrorCallback(
error_callback, service_path, error_name, "");
}
bool IsAuthenticationError(const std::string& error) {
return (error == shill::kErrorBadWEPKey ||
error == shill::kErrorPppAuthFailed ||
error == shill::kErrorEapLocalTlsFailed ||
error == shill::kErrorEapRemoteTlsFailed ||
error == shill::kErrorEapAuthenticationFailed);
}
bool VPNRequiresCredentials(const std::string& service_path,
const std::string& provider_type,
const base::DictionaryValue& provider_properties) {
if (provider_type == shill::kProviderOpenVpn) {
std::string username;
provider_properties.GetStringWithoutPathExpansion(
shill::kOpenVPNUserProperty, &username);
if (username.empty()) {
NET_LOG_EVENT("OpenVPN: No username", service_path);
return true;
}
bool passphrase_required = false;
provider_properties.GetBooleanWithoutPathExpansion(
shill::kPassphraseRequiredProperty, &passphrase_required);
if (passphrase_required) {
NET_LOG_EVENT("OpenVPN: Passphrase Required", service_path);
return true;
}
NET_LOG_EVENT("OpenVPN Is Configured", service_path);
} else {
bool passphrase_required = false;
provider_properties.GetBooleanWithoutPathExpansion(
shill::kL2tpIpsecPskRequiredProperty, &passphrase_required);
if (passphrase_required) {
NET_LOG_EVENT("VPN: PSK Required", service_path);
return true;
}
provider_properties.GetBooleanWithoutPathExpansion(
shill::kPassphraseRequiredProperty, &passphrase_required);
if (passphrase_required) {
NET_LOG_EVENT("VPN: Passphrase Required", service_path);
return true;
}
NET_LOG_EVENT("VPN Is Configured", service_path);
}
return false;
}
std::string GetDefaultUserProfilePath(const NetworkState* network) {
if (!NetworkHandler::IsInitialized() ||
!LoginState::Get()->IsUserAuthenticated() ||
(network && network->type() == shill::kTypeWifi &&
network->security() == shill::kSecurityNone)) {
return NetworkProfileHandler::GetSharedProfilePath();
}
const NetworkProfile* profile =
NetworkHandler::Get()->network_profile_handler()->GetDefaultUserProfile();
return profile ? profile->path
: NetworkProfileHandler::GetSharedProfilePath();
}
}
const char NetworkConnectionHandler::kErrorNotFound[] = "not-found";
const char NetworkConnectionHandler::kErrorConnected[] = "connected";
const char NetworkConnectionHandler::kErrorConnecting[] = "connecting";
const char NetworkConnectionHandler::kErrorNotConnected[] = "not-connected";
const char NetworkConnectionHandler::kErrorPassphraseRequired[] =
"passphrase-required";
const char NetworkConnectionHandler::kErrorActivationRequired[] =
"activation-required";
const char NetworkConnectionHandler::kErrorCertificateRequired[] =
"certificate-required";
const char NetworkConnectionHandler::kErrorConfigurationRequired[] =
"configuration-required";
const char NetworkConnectionHandler::kErrorAuthenticationRequired[] =
"authentication-required";
const char NetworkConnectionHandler::kErrorShillError[] = "shill-error";
const char NetworkConnectionHandler::kErrorConfigureFailed[] =
"configure-failed";
const char NetworkConnectionHandler::kErrorConnectCanceled[] =
"connect-canceled";
const char NetworkConnectionHandler::kErrorCertLoadTimeout[] =
"cert-load-timeout";
struct NetworkConnectionHandler::ConnectRequest {
ConnectRequest(const std::string& service_path,
const std::string& profile_path,
const base::Closure& success,
const network_handler::ErrorCallback& error)
: service_path(service_path),
profile_path(profile_path),
connect_state(CONNECT_REQUESTED),
success_callback(success),
error_callback(error) {
}
enum ConnectState {
CONNECT_REQUESTED = 0,
CONNECT_STARTED = 1,
CONNECT_CONNECTING = 2
};
std::string service_path;
std::string profile_path;
ConnectState connect_state;
base::Closure success_callback;
network_handler::ErrorCallback error_callback;
};
NetworkConnectionHandler::NetworkConnectionHandler()
: cert_loader_(NULL),
network_state_handler_(NULL),
network_configuration_handler_(NULL),
logged_in_(false),
certificates_loaded_(false) {
}
NetworkConnectionHandler::~NetworkConnectionHandler() {
if (network_state_handler_)
network_state_handler_->RemoveObserver(this, FROM_HERE);
if (cert_loader_)
cert_loader_->RemoveObserver(this);
if (LoginState::IsInitialized())
LoginState::Get()->RemoveObserver(this);
}
void NetworkConnectionHandler::Init(
NetworkStateHandler* network_state_handler,
NetworkConfigurationHandler* network_configuration_handler) {
if (LoginState::IsInitialized()) {
LoginState::Get()->AddObserver(this);
logged_in_ = LoginState::Get()->IsUserLoggedIn();
logged_in_time_ = base::TimeTicks::Now();
}
if (CertLoader::IsInitialized()) {
cert_loader_ = CertLoader::Get();
cert_loader_->AddObserver(this);
if (cert_loader_->certificates_loaded()) {
NET_LOG_EVENT("Certificates Loaded", "");
certificates_loaded_ = true;
}
} else {
NET_LOG_EVENT("Certificate Loader not initialized", "");
certificates_loaded_ = true;
}
if (network_state_handler) {
network_state_handler_ = network_state_handler;
network_state_handler_->AddObserver(this, FROM_HERE);
}
network_configuration_handler_ = network_configuration_handler;
}
void NetworkConnectionHandler::LoggedInStateChanged() {
if (LoginState::Get()->IsUserLoggedIn()) {
logged_in_ = true;
NET_LOG_EVENT("Logged In", "");
logged_in_time_ = base::TimeTicks::Now();
}
}
void NetworkConnectionHandler::OnCertificatesLoaded(
const net::CertificateList& cert_list,
bool initial_load) {
certificates_loaded_ = true;
NET_LOG_EVENT("Certificates Loaded", "");
if (queued_connect_) {
ConnectToQueuedNetwork();
} else if (initial_load) {
network_state_handler_->ConnectToBestWifiNetwork();
}
}
void NetworkConnectionHandler::ConnectToNetwork(
const std::string& service_path,
const base::Closure& success_callback,
const network_handler::ErrorCallback& error_callback,
bool check_error_state) {
NET_LOG_USER("ConnectToNetwork", service_path);
queued_connect_.reset();
if (HasConnectingNetwork(service_path)) {
NET_LOG_USER("Connect Request While Pending", service_path);
InvokeErrorCallback(service_path, error_callback, kErrorConnecting);
return;
}
const NetworkState* network =
network_state_handler_->GetNetworkState(service_path);
if (network) {
if (network->IsConnectedState()) {
InvokeErrorCallback(service_path, error_callback, kErrorConnected);
return;
}
if (network->IsConnectingState()) {
InvokeErrorCallback(service_path, error_callback, kErrorConnecting);
return;
}
if (network->RequiresActivation()) {
InvokeErrorCallback(service_path, error_callback,
kErrorActivationRequired);
return;
}
if (check_error_state) {
const std::string& error = network->last_error();
if (error == shill::kErrorBadPassphrase) {
InvokeErrorCallback(service_path, error_callback, error);
return;
}
if (IsAuthenticationError(error)) {
InvokeErrorCallback(
service_path, error_callback, kErrorAuthenticationRequired);
return;
}
}
}
std::string profile_path;
if (!network || network->profile_path().empty())
profile_path = GetDefaultUserProfilePath(network);
pending_requests_.insert(std::make_pair(
service_path,
ConnectRequest(service_path, profile_path,
success_callback, error_callback)));
if (network && network->connectable() && network->type() != shill::kTypeVPN) {
CallShillConnect(service_path);
return;
}
network_configuration_handler_->GetProperties(
service_path,
base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect,
AsWeakPtr(), check_error_state),
base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
AsWeakPtr(), service_path));
}
void NetworkConnectionHandler::DisconnectNetwork(
const std::string& service_path,
const base::Closure& success_callback,
const network_handler::ErrorCallback& error_callback) {
NET_LOG_USER("DisconnectNetwork", service_path);
const NetworkState* network =
network_state_handler_->GetNetworkState(service_path);
if (!network) {
InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
return;
}
if (!network->IsConnectedState()) {
InvokeErrorCallback(service_path, error_callback, kErrorNotConnected);
return;
}
CallShillDisconnect(service_path, success_callback, error_callback);
}
bool NetworkConnectionHandler::HasConnectingNetwork(
const std::string& service_path) {
return pending_requests_.count(service_path) != 0;
}
bool NetworkConnectionHandler::HasPendingConnectRequest() {
return pending_requests_.size() > 0;
}
void NetworkConnectionHandler::NetworkListChanged() {
CheckAllPendingRequests();
}
void NetworkConnectionHandler::NetworkPropertiesUpdated(
const NetworkState* network) {
if (HasConnectingNetwork(network->path()))
CheckPendingRequest(network->path());
}
NetworkConnectionHandler::ConnectRequest*
NetworkConnectionHandler::GetPendingRequest(const std::string& service_path) {
std::map<std::string, ConnectRequest>::iterator iter =
pending_requests_.find(service_path);
return iter != pending_requests_.end() ? &(iter->second) : NULL;
}
void NetworkConnectionHandler::VerifyConfiguredAndConnect(
bool check_error_state,
const std::string& service_path,
const base::DictionaryValue& service_properties) {
NET_LOG_EVENT("VerifyConfiguredAndConnect", service_path);
bool passphrase_required = false;
service_properties.GetBooleanWithoutPathExpansion(
shill::kPassphraseRequiredProperty, &passphrase_required);
if (passphrase_required) {
ErrorCallbackForPendingRequest(service_path, kErrorPassphraseRequired);
return;
}
std::string type, security;
service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
service_properties.GetStringWithoutPathExpansion(
shill::kSecurityProperty, &security);
bool connectable = false;
service_properties.GetBooleanWithoutPathExpansion(
shill::kConnectableProperty, &connectable);
if (connectable && type != shill::kTypeVPN) {
CallShillConnect(service_path);
return;
}
const base::DictionaryValue* provider_properties = NULL;
std::string vpn_provider_type, vpn_provider_host, vpn_client_cert_id;
if (type == shill::kTypeVPN) {
if (service_properties.GetDictionaryWithoutPathExpansion(
shill::kProviderProperty, &provider_properties)) {
provider_properties->GetStringWithoutPathExpansion(
shill::kTypeProperty, &vpn_provider_type);
provider_properties->GetStringWithoutPathExpansion(
shill::kHostProperty, &vpn_provider_host);
provider_properties->GetStringWithoutPathExpansion(
shill::kL2tpIpsecClientCertIdProperty, &vpn_client_cert_id);
}
if (vpn_provider_type.empty() || vpn_provider_host.empty()) {
ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
return;
}
}
scoped_ptr<NetworkUIData> ui_data =
shill_property_util::GetUIDataFromProperties(service_properties);
client_cert::ConfigType client_cert_type = client_cert::CONFIG_TYPE_NONE;
if (type == shill::kTypeVPN) {
if (vpn_provider_type == shill::kProviderOpenVpn) {
client_cert_type = client_cert::CONFIG_TYPE_OPENVPN;
} else {
if (!vpn_client_cert_id.empty() ||
(ui_data && ui_data->certificate_type() != CLIENT_CERT_TYPE_NONE))
client_cert_type = client_cert::CONFIG_TYPE_IPSEC;
}
} else if (type == shill::kTypeWifi && security == shill::kSecurity8021x) {
client_cert_type = client_cert::CONFIG_TYPE_EAP;
}
base::DictionaryValue config_properties;
if (client_cert_type != client_cert::CONFIG_TYPE_NONE) {
if (!logged_in_ || !cert_loader_) {
NET_LOG_ERROR("User not logged in", "");
ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired);
return;
}
if (!certificates_loaded_) {
NET_LOG_EVENT("Certificates not loaded", "");
QueueConnectRequest(service_path);
return;
}
std::string pkcs11_id;
if (ui_data && ui_data->certificate_type() == CLIENT_CERT_TYPE_PATTERN) {
pkcs11_id = CertificateIsConfigured(ui_data.get());
if (!cert_loader_->IsHardwareBacked() || pkcs11_id.empty()) {
ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired);
return;
}
} else if (check_error_state &&
!client_cert::IsCertificateConfigured(client_cert_type,
service_properties)) {
ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
return;
}
if (cert_loader_ && cert_loader_->IsHardwareBacked()) {
client_cert::SetShillProperties(
client_cert_type,
base::IntToString(cert_loader_->TPMTokenSlotID()),
TPMTokenLoader::Get()->tpm_user_pin(),
pkcs11_id.empty() ? NULL : &pkcs11_id,
&config_properties);
}
}
if (type == shill::kTypeVPN) {
DCHECK(provider_properties);
if (VPNRequiresCredentials(
service_path, vpn_provider_type, *provider_properties)) {
NET_LOG_USER("VPN Requires Credentials", service_path);
ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
return;
}
if (client_cert_type == client_cert::CONFIG_TYPE_NONE) {
CallShillConnect(service_path);
return;
}
}
if (!config_properties.empty()) {
NET_LOG_EVENT("Configuring Network", service_path);
network_configuration_handler_->SetProperties(
service_path,
config_properties,
base::Bind(&NetworkConnectionHandler::CallShillConnect,
AsWeakPtr(),
service_path),
base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
AsWeakPtr(),
service_path));
return;
}
if (check_error_state)
ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
else
CallShillConnect(service_path);
}
void NetworkConnectionHandler::QueueConnectRequest(
const std::string& service_path) {
ConnectRequest* request = GetPendingRequest(service_path);
if (!request) {
NET_LOG_ERROR("No pending request to queue", service_path);
return;
}
const int kMaxCertLoadTimeSeconds = 15;
base::TimeDelta dtime = base::TimeTicks::Now() - logged_in_time_;
if (dtime > base::TimeDelta::FromSeconds(kMaxCertLoadTimeSeconds)) {
NET_LOG_ERROR("Certificate load timeout", service_path);
InvokeErrorCallback(service_path,
request->error_callback,
kErrorCertLoadTimeout);
return;
}
NET_LOG_EVENT("Connect Request Queued", service_path);
queued_connect_.reset(new ConnectRequest(
service_path, request->profile_path,
request->success_callback, request->error_callback));
pending_requests_.erase(service_path);
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&NetworkConnectionHandler::CheckCertificatesLoaded,
AsWeakPtr()),
base::TimeDelta::FromSeconds(kMaxCertLoadTimeSeconds) - dtime);
}
void NetworkConnectionHandler::CheckCertificatesLoaded() {
if (certificates_loaded_)
return;
if (!queued_connect_)
return;
NET_LOG_ERROR("Certificate load timeout", queued_connect_->service_path);
InvokeErrorCallback(queued_connect_->service_path,
queued_connect_->error_callback,
kErrorCertLoadTimeout);
queued_connect_.reset();
}
void NetworkConnectionHandler::ConnectToQueuedNetwork() {
DCHECK(queued_connect_);
std::string service_path = queued_connect_->service_path;
base::Closure success_callback = queued_connect_->success_callback;
network_handler::ErrorCallback error_callback =
queued_connect_->error_callback;
NET_LOG_EVENT("Connecting to Queued Network", service_path);
ConnectToNetwork(service_path, success_callback, error_callback,
false );
}
void NetworkConnectionHandler::CallShillConnect(
const std::string& service_path) {
NET_LOG_EVENT("Sending Connect Request to Shill", service_path);
network_state_handler_->ClearLastErrorForNetwork(service_path);
DBusThreadManager::Get()->GetShillServiceClient()->Connect(
dbus::ObjectPath(service_path),
base::Bind(&NetworkConnectionHandler::HandleShillConnectSuccess,
AsWeakPtr(), service_path),
base::Bind(&NetworkConnectionHandler::HandleShillConnectFailure,
AsWeakPtr(), service_path));
}
void NetworkConnectionHandler::HandleConfigurationFailure(
const std::string& service_path,
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
ConnectRequest* request = GetPendingRequest(service_path);
if (!request) {
NET_LOG_ERROR("HandleConfigurationFailure called with no pending request.",
service_path);
return;
}
network_handler::ErrorCallback error_callback = request->error_callback;
pending_requests_.erase(service_path);
if (!error_callback.is_null())
error_callback.Run(kErrorConfigureFailed, error_data.Pass());
}
void NetworkConnectionHandler::HandleShillConnectSuccess(
const std::string& service_path) {
ConnectRequest* request = GetPendingRequest(service_path);
if (!request) {
NET_LOG_ERROR("HandleShillConnectSuccess called with no pending request.",
service_path);
return;
}
request->connect_state = ConnectRequest::CONNECT_STARTED;
NET_LOG_EVENT("Connect Request Acknowledged", service_path);
CheckPendingRequest(service_path);
}
void NetworkConnectionHandler::HandleShillConnectFailure(
const std::string& service_path,
const std::string& dbus_error_name,
const std::string& dbus_error_message) {
ConnectRequest* request = GetPendingRequest(service_path);
if (!request) {
NET_LOG_ERROR("HandleShillConnectFailure called with no pending request.",
service_path);
return;
}
network_handler::ErrorCallback error_callback = request->error_callback;
pending_requests_.erase(service_path);
network_handler::ShillErrorCallbackFunction(
shill::kErrorConnectFailed, service_path, error_callback,
dbus_error_name, dbus_error_message);
}
void NetworkConnectionHandler::CheckPendingRequest(
const std::string service_path) {
ConnectRequest* request = GetPendingRequest(service_path);
DCHECK(request);
if (request->connect_state == ConnectRequest::CONNECT_REQUESTED)
return;
const NetworkState* network =
network_state_handler_->GetNetworkState(service_path);
if (!network)
return;
if (network->IsConnectingState()) {
request->connect_state = ConnectRequest::CONNECT_CONNECTING;
return;
}
if (network->IsConnectedState()) {
NET_LOG_EVENT("Connect Request Succeeded", service_path);
if (!request->profile_path.empty()) {
network_configuration_handler_->SetNetworkProfile(
service_path, request->profile_path,
base::Bind(&base::DoNothing),
chromeos::network_handler::ErrorCallback());
}
if (!request->success_callback.is_null())
request->success_callback.Run();
pending_requests_.erase(service_path);
return;
}
if (network->connection_state() == shill::kStateIdle &&
request->connect_state != ConnectRequest::CONNECT_CONNECTING) {
return;
}
std::string error_name;
if (network->connection_state() == shill::kStateIdle &&
pending_requests_.size() > 1) {
error_name = kErrorConnectCanceled;
} else {
error_name = shill::kErrorConnectFailed;
if (network->connection_state() != shill::kStateFailure) {
NET_LOG_ERROR("Unexpected State: " + network->connection_state(),
service_path);
}
}
network_handler::ErrorCallback error_callback = request->error_callback;
pending_requests_.erase(service_path);
if (error_callback.is_null()) {
NET_LOG_ERROR("Connect Error, no callback: " + error_name, service_path);
return;
}
InvokeErrorCallback(service_path, error_callback, error_name);
}
void NetworkConnectionHandler::CheckAllPendingRequests() {
for (std::map<std::string, ConnectRequest>::iterator iter =
pending_requests_.begin(); iter != pending_requests_.end(); ++iter) {
CheckPendingRequest(iter->first);
}
}
std::string NetworkConnectionHandler::CertificateIsConfigured(
NetworkUIData* ui_data) {
if (ui_data->certificate_pattern().Empty())
return std::string();
scoped_refptr<net::X509Certificate> matching_cert =
client_cert::GetCertificateMatch(ui_data->certificate_pattern(),
cert_loader_->cert_list());
if (!matching_cert.get())
return std::string();
return CertLoader::GetPkcs11IdForCert(*matching_cert.get());
}
void NetworkConnectionHandler::ErrorCallbackForPendingRequest(
const std::string& service_path,
const std::string& error_name) {
ConnectRequest* request = GetPendingRequest(service_path);
if (!request) {
NET_LOG_ERROR("ErrorCallbackForPendingRequest with no pending request.",
service_path);
return;
}
network_handler::ErrorCallback error_callback = request->error_callback;
pending_requests_.erase(service_path);
InvokeErrorCallback(service_path, error_callback, error_name);
}
void NetworkConnectionHandler::CallShillDisconnect(
const std::string& service_path,
const base::Closure& success_callback,
const network_handler::ErrorCallback& error_callback) {
NET_LOG_USER("Disconnect Request", service_path);
DBusThreadManager::Get()->GetShillServiceClient()->Disconnect(
dbus::ObjectPath(service_path),
base::Bind(&NetworkConnectionHandler::HandleShillDisconnectSuccess,
AsWeakPtr(), service_path, success_callback),
base::Bind(&network_handler::ShillErrorCallbackFunction,
kErrorShillError, service_path, error_callback));
}
void NetworkConnectionHandler::HandleShillDisconnectSuccess(
const std::string& service_path,
const base::Closure& success_callback) {
NET_LOG_EVENT("Disconnect Request Sent", service_path);
if (!success_callback.is_null())
success_callback.Run();
}
}