Home | History | Annotate | Download | only in network
      1 // Copyright (c) 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/network_connection_handler.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/json/json_reader.h"
     10 #include "chromeos/chromeos_switches.h"
     11 #include "chromeos/dbus/dbus_thread_manager.h"
     12 #include "chromeos/dbus/shill_manager_client.h"
     13 #include "chromeos/dbus/shill_service_client.h"
     14 #include "chromeos/network/client_cert_util.h"
     15 #include "chromeos/network/managed_network_configuration_handler.h"
     16 #include "chromeos/network/network_configuration_handler.h"
     17 #include "chromeos/network/network_event_log.h"
     18 #include "chromeos/network/network_handler_callbacks.h"
     19 #include "chromeos/network/network_state.h"
     20 #include "chromeos/network/network_state_handler.h"
     21 #include "chromeos/network/network_ui_data.h"
     22 #include "dbus/object_path.h"
     23 #include "net/cert/x509_certificate.h"
     24 #include "third_party/cros_system_api/dbus/service_constants.h"
     25 
     26 namespace chromeos {
     27 
     28 namespace {
     29 
     30 void InvokeErrorCallback(const std::string& service_path,
     31                          const network_handler::ErrorCallback& error_callback,
     32                          const std::string& error_name) {
     33   std::string error_msg = "Connect Error: " + error_name;
     34   NET_LOG_ERROR(error_msg, service_path);
     35   if (error_callback.is_null())
     36     return;
     37   scoped_ptr<base::DictionaryValue> error_data(
     38       network_handler::CreateErrorData(service_path, error_name, error_msg));
     39   error_callback.Run(error_name, error_data.Pass());
     40 }
     41 
     42 bool IsAuthenticationError(const std::string& error) {
     43   return (error == flimflam::kErrorBadWEPKey ||
     44           error == flimflam::kErrorPppAuthFailed ||
     45           error == shill::kErrorEapLocalTlsFailed ||
     46           error == shill::kErrorEapRemoteTlsFailed ||
     47           error == shill::kErrorEapAuthenticationFailed);
     48 }
     49 
     50 void CopyStringFromDictionary(const base::DictionaryValue& source,
     51                               const std::string& key,
     52                               base::DictionaryValue* dest) {
     53   std::string string_value;
     54   if (source.GetStringWithoutPathExpansion(key, &string_value))
     55     dest->SetStringWithoutPathExpansion(key, string_value);
     56 }
     57 
     58 bool NetworkRequiresActivation(const NetworkState* network) {
     59   return (network->type() == flimflam::kTypeCellular &&
     60       ((network->activation_state() != flimflam::kActivationStateActivated &&
     61         network->activation_state() != flimflam::kActivationStateUnknown)));
     62 }
     63 
     64 bool VPNIsConfigured(const std::string& service_path,
     65                      const std::string& provider_type,
     66                      const base::DictionaryValue& provider_properties) {
     67   if (provider_type == flimflam::kProviderOpenVpn) {
     68     std::string hostname;
     69     provider_properties.GetStringWithoutPathExpansion(
     70         flimflam::kHostProperty, &hostname);
     71     if (hostname.empty()) {
     72       NET_LOG_EVENT("OpenVPN: No hostname", service_path);
     73       return false;
     74     }
     75     std::string username;
     76     provider_properties.GetStringWithoutPathExpansion(
     77         flimflam::kOpenVPNUserProperty, &username);
     78     if (username.empty()) {
     79       NET_LOG_EVENT("OpenVPN: No username", service_path);
     80       return false;
     81     }
     82     bool passphrase_required = false;
     83     provider_properties.GetBooleanWithoutPathExpansion(
     84         flimflam::kPassphraseRequiredProperty, &passphrase_required);
     85     if (passphrase_required) {
     86       NET_LOG_EVENT("OpenVPN: Passphrase Required", service_path);
     87       return false;
     88     }
     89     NET_LOG_EVENT("OpenVPN Is Configured", service_path);
     90   } else {
     91     bool passphrase_required = false;
     92     std::string passphrase;
     93     provider_properties.GetBooleanWithoutPathExpansion(
     94         flimflam::kL2tpIpsecPskRequiredProperty, &passphrase_required);
     95     if (passphrase_required) {
     96       NET_LOG_EVENT("VPN: Passphrase Required", service_path);
     97       return false;
     98     }
     99     NET_LOG_EVENT("VPN Is Configured", service_path);
    100   }
    101   return true;
    102 }
    103 
    104 }  // namespace
    105 
    106 const char NetworkConnectionHandler::kErrorNotFound[] = "not-found";
    107 const char NetworkConnectionHandler::kErrorConnected[] = "connected";
    108 const char NetworkConnectionHandler::kErrorConnecting[] = "connecting";
    109 const char NetworkConnectionHandler::kErrorNotConnected[] = "not-connected";
    110 const char NetworkConnectionHandler::kErrorPassphraseRequired[] =
    111     "passphrase-required";
    112 const char NetworkConnectionHandler::kErrorActivationRequired[] =
    113     "activation-required";
    114 const char NetworkConnectionHandler::kErrorCertificateRequired[] =
    115     "certificate-required";
    116 const char NetworkConnectionHandler::kErrorConfigurationRequired[] =
    117     "configuration-required";
    118 const char NetworkConnectionHandler::kErrorAuthenticationRequired[] =
    119     "authentication-required";
    120 const char NetworkConnectionHandler::kErrorShillError[] = "shill-error";
    121 const char NetworkConnectionHandler::kErrorConfigureFailed[] =
    122     "configure-failed";
    123 const char NetworkConnectionHandler::kErrorConnectCanceled[] =
    124     "connect-canceled";
    125 
    126 struct NetworkConnectionHandler::ConnectRequest {
    127   ConnectRequest(const std::string& service_path,
    128                  const base::Closure& success,
    129                  const network_handler::ErrorCallback& error)
    130       : service_path(service_path),
    131         connect_state(CONNECT_REQUESTED),
    132         success_callback(success),
    133         error_callback(error) {
    134   }
    135   enum ConnectState {
    136     CONNECT_REQUESTED = 0,
    137     CONNECT_STARTED = 1,
    138     CONNECT_CONNECTING = 2
    139   };
    140   std::string service_path;
    141   ConnectState connect_state;
    142   base::Closure success_callback;
    143   network_handler::ErrorCallback error_callback;
    144 };
    145 
    146 NetworkConnectionHandler::NetworkConnectionHandler()
    147     : cert_loader_(NULL),
    148       network_state_handler_(NULL),
    149       network_configuration_handler_(NULL),
    150       logged_in_(false),
    151       certificates_loaded_(false) {
    152 }
    153 
    154 NetworkConnectionHandler::~NetworkConnectionHandler() {
    155   if (network_state_handler_)
    156     network_state_handler_->RemoveObserver(this, FROM_HERE);
    157   if (cert_loader_)
    158     cert_loader_->RemoveObserver(this);
    159   if (LoginState::IsInitialized())
    160     LoginState::Get()->RemoveObserver(this);
    161 }
    162 
    163 void NetworkConnectionHandler::Init(
    164     NetworkStateHandler* network_state_handler,
    165     NetworkConfigurationHandler* network_configuration_handler) {
    166   if (LoginState::IsInitialized()) {
    167     LoginState::Get()->AddObserver(this);
    168     logged_in_ =
    169         LoginState::Get()->GetLoggedInState() == LoginState::LOGGED_IN_ACTIVE;
    170   }
    171   if (CertLoader::IsInitialized()) {
    172     cert_loader_ = CertLoader::Get();
    173     cert_loader_->AddObserver(this);
    174     certificates_loaded_ = cert_loader_->certificates_loaded();
    175   } else {
    176     // TODO(stevenjb): Require a mock or stub cert_loader in tests.
    177     certificates_loaded_ = true;
    178   }
    179   if (network_state_handler) {
    180     network_state_handler_ = network_state_handler;
    181     network_state_handler_->AddObserver(this, FROM_HERE);
    182   }
    183   network_configuration_handler_ = network_configuration_handler;
    184 }
    185 
    186 void NetworkConnectionHandler::LoggedInStateChanged(
    187     LoginState::LoggedInState state) {
    188   if (state == LoginState::LOGGED_IN_ACTIVE) {
    189     logged_in_ = true;
    190     NET_LOG_EVENT("Logged In", "");
    191   }
    192 }
    193 
    194 void NetworkConnectionHandler::OnCertificatesLoaded(
    195     const net::CertificateList& cert_list,
    196     bool initial_load) {
    197   certificates_loaded_ = true;
    198   NET_LOG_EVENT("Certificates Loaded", "");
    199   if (queued_connect_) {
    200     NET_LOG_EVENT("Connecting to Queued Network",
    201                   queued_connect_->service_path);
    202     ConnectToNetwork(queued_connect_->service_path,
    203                      queued_connect_->success_callback,
    204                      queued_connect_->error_callback,
    205                      false /* check_error_state */);
    206   } else if (initial_load) {
    207     // Once certificates have loaded, connect to the "best" available network.
    208     network_state_handler_->ConnectToBestWifiNetwork();
    209   }
    210 }
    211 
    212 void NetworkConnectionHandler::ConnectToNetwork(
    213     const std::string& service_path,
    214     const base::Closure& success_callback,
    215     const network_handler::ErrorCallback& error_callback,
    216     bool check_error_state) {
    217   NET_LOG_USER("ConnectToNetwork", service_path);
    218   // Clear any existing queued connect request.
    219   queued_connect_.reset();
    220   if (HasConnectingNetwork(service_path)) {
    221     NET_LOG_USER("Connect Request While Pending", service_path);
    222     InvokeErrorCallback(service_path, error_callback, kErrorConnecting);
    223     return;
    224   }
    225 
    226   // Check cached network state for connected, connecting, or unactivated
    227   // networks. These states will not be affected by a recent configuration.
    228   // Note: NetworkState may not exist for a network that was recently
    229   // configured, in which case these checks do not apply anyway.
    230   const NetworkState* network =
    231       network_state_handler_->GetNetworkState(service_path);
    232 
    233   if (network) {
    234     // For existing networks, perform some immediate consistency checks.
    235     if (network->IsConnectedState()) {
    236       InvokeErrorCallback(service_path, error_callback, kErrorConnected);
    237       return;
    238     }
    239     if (network->IsConnectingState()) {
    240       InvokeErrorCallback(service_path, error_callback, kErrorConnecting);
    241       return;
    242     }
    243     if (NetworkRequiresActivation(network)) {
    244       InvokeErrorCallback(service_path, error_callback,
    245                           kErrorActivationRequired);
    246       return;
    247     }
    248 
    249     if (check_error_state) {
    250       const std::string& error = network->error();
    251       if (error == flimflam::kErrorBadPassphrase) {
    252         InvokeErrorCallback(service_path, error_callback, error);
    253         return;
    254       }
    255       if (IsAuthenticationError(error)) {
    256         InvokeErrorCallback(
    257             service_path, error_callback, kErrorAuthenticationRequired);
    258         return;
    259       }
    260     }
    261   }
    262 
    263   // All synchronous checks passed, add |service_path| to connecting list.
    264   pending_requests_.insert(std::make_pair(
    265       service_path,
    266       ConnectRequest(service_path, success_callback, error_callback)));
    267 
    268   // Connect immediately to 'connectable' networks.
    269   // TODO(stevenjb): Shill needs to properly set Connectable for VPN.
    270   if (network &&
    271       network->connectable() && network->type() != flimflam::kTypeVPN) {
    272     CallShillConnect(service_path);
    273     return;
    274   }
    275 
    276   // Request additional properties to check. VerifyConfiguredAndConnect will
    277   // use only these properties, not cached properties, to ensure that they
    278   // are up to date after any recent configuration.
    279   network_configuration_handler_->GetProperties(
    280       service_path,
    281       base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect,
    282                  AsWeakPtr(), check_error_state),
    283       base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
    284                  AsWeakPtr(), service_path));
    285 }
    286 
    287 void NetworkConnectionHandler::DisconnectNetwork(
    288     const std::string& service_path,
    289     const base::Closure& success_callback,
    290     const network_handler::ErrorCallback& error_callback) {
    291   NET_LOG_USER("DisconnectNetwork", service_path);
    292   const NetworkState* network =
    293       network_state_handler_->GetNetworkState(service_path);
    294   if (!network) {
    295     InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
    296     return;
    297   }
    298   if (!network->IsConnectedState()) {
    299     InvokeErrorCallback(service_path, error_callback, kErrorNotConnected);
    300     return;
    301   }
    302   CallShillDisconnect(service_path, success_callback, error_callback);
    303 }
    304 
    305 void NetworkConnectionHandler::ActivateNetwork(
    306     const std::string& service_path,
    307     const std::string& carrier,
    308     const base::Closure& success_callback,
    309     const network_handler::ErrorCallback& error_callback) {
    310   NET_LOG_USER("DisconnectNetwork", service_path);
    311   const NetworkState* network =
    312       network_state_handler_->GetNetworkState(service_path);
    313   if (!network) {
    314     InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
    315     return;
    316   }
    317   CallShillActivate(service_path, carrier, success_callback, error_callback);
    318 }
    319 
    320 bool NetworkConnectionHandler::HasConnectingNetwork(
    321     const std::string& service_path) {
    322   return pending_requests_.count(service_path) != 0;
    323 }
    324 
    325 void NetworkConnectionHandler::NetworkListChanged() {
    326   CheckAllPendingRequests();
    327 }
    328 
    329 void NetworkConnectionHandler::NetworkPropertiesUpdated(
    330     const NetworkState* network) {
    331   if (HasConnectingNetwork(network->path()))
    332     CheckPendingRequest(network->path());
    333 }
    334 
    335 NetworkConnectionHandler::ConnectRequest*
    336 NetworkConnectionHandler::GetPendingRequest(const std::string& service_path) {
    337   std::map<std::string, ConnectRequest>::iterator iter =
    338       pending_requests_.find(service_path);
    339   return iter != pending_requests_.end() ? &(iter->second) : NULL;
    340 }
    341 
    342 // ConnectToNetwork implementation
    343 
    344 void NetworkConnectionHandler::VerifyConfiguredAndConnect(
    345     bool check_error_state,
    346     const std::string& service_path,
    347     const base::DictionaryValue& service_properties) {
    348   NET_LOG_EVENT("VerifyConfiguredAndConnect", service_path);
    349 
    350   // If 'passphrase_required' is still true, then the 'Passphrase' property
    351   // has not been set to a minimum length value.
    352   bool passphrase_required = false;
    353   service_properties.GetBooleanWithoutPathExpansion(
    354       flimflam::kPassphraseRequiredProperty, &passphrase_required);
    355   if (passphrase_required) {
    356     ErrorCallbackForPendingRequest(service_path, kErrorPassphraseRequired);
    357     return;
    358   }
    359 
    360   std::string type, security;
    361   service_properties.GetStringWithoutPathExpansion(
    362       flimflam::kTypeProperty, &type);
    363   service_properties.GetStringWithoutPathExpansion(
    364       flimflam::kSecurityProperty, &security);
    365   bool connectable = false;
    366   service_properties.GetBooleanWithoutPathExpansion(
    367       flimflam::kConnectableProperty, &connectable);
    368 
    369   // In case NetworkState was not available in ConnectToNetwork (e.g. it had
    370   // been recently configured), we need to check Connectable again.
    371   if (connectable && type != flimflam::kTypeVPN) {
    372     // TODO(stevenjb): Shill needs to properly set Connectable for VPN.
    373     CallShillConnect(service_path);
    374     return;
    375   }
    376 
    377   // Get VPN provider type and host (required for configuration) and ensure
    378   // that required VPN non-cert properties are set.
    379   std::string vpn_provider_type, vpn_provider_host;
    380   if (type == flimflam::kTypeVPN) {
    381     // VPN Provider values are read from the "Provider" dictionary, not the
    382     // "Provider.Type", etc keys (which are used only to set the values).
    383     const base::DictionaryValue* provider_properties;
    384     if (service_properties.GetDictionaryWithoutPathExpansion(
    385             flimflam::kProviderProperty, &provider_properties)) {
    386       provider_properties->GetStringWithoutPathExpansion(
    387           flimflam::kTypeProperty, &vpn_provider_type);
    388       provider_properties->GetStringWithoutPathExpansion(
    389           flimflam::kHostProperty, &vpn_provider_host);
    390     }
    391     if (vpn_provider_type.empty() || vpn_provider_host.empty()) {
    392       ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
    393       return;
    394     }
    395     // VPN requires a host and username to be set.
    396     if (!VPNIsConfigured(
    397             service_path, vpn_provider_type, *provider_properties)) {
    398       NET_LOG_ERROR("VPN Not Configured", service_path);
    399       ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
    400       return;
    401     }
    402   }
    403 
    404   client_cert::ConfigType client_cert_type = client_cert::CONFIG_TYPE_NONE;
    405   if (type == flimflam::kTypeVPN) {
    406     if (vpn_provider_type == flimflam::kProviderOpenVpn)
    407       client_cert_type = client_cert::CONFIG_TYPE_OPENVPN;
    408     else
    409       client_cert_type = client_cert::CONFIG_TYPE_IPSEC;
    410   } else if (type == flimflam::kTypeWifi &&
    411              security == flimflam::kSecurity8021x) {
    412     client_cert_type = client_cert::CONFIG_TYPE_EAP;
    413   }
    414 
    415   base::DictionaryValue config_properties;
    416   if (client_cert_type != client_cert::CONFIG_TYPE_NONE) {
    417     // If the client certificate must be configured, this will be set to a
    418     // non-empty string.
    419     std::string pkcs11_id;
    420 
    421     // Check certificate properties in kUIDataProperty if configured.
    422     // Note: Wifi/VPNConfigView set these properties explicitly, in which case
    423     //   only the TPM must be configured.
    424     scoped_ptr<NetworkUIData> ui_data =
    425         ManagedNetworkConfigurationHandler::GetUIData(service_properties);
    426     if (ui_data && ui_data->certificate_type() == CLIENT_CERT_TYPE_PATTERN) {
    427       // User must be logged in to connect to a network requiring a certificate.
    428       if (!logged_in_ || !cert_loader_) {
    429         ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired);
    430         return;
    431       }
    432 
    433       // If certificates have not been loaded yet, queue the connect request.
    434       if (!certificates_loaded_) {
    435         ConnectRequest* request = GetPendingRequest(service_path);
    436         if (!request) {
    437           NET_LOG_ERROR("No pending request to queue", service_path);
    438           return;
    439         }
    440         NET_LOG_EVENT("Connect Request Queued", service_path);
    441         queued_connect_.reset(new ConnectRequest(
    442             service_path, request->success_callback, request->error_callback));
    443         pending_requests_.erase(service_path);
    444         return;
    445       }
    446 
    447       pkcs11_id = CertificateIsConfigured(ui_data.get());
    448       // Ensure the certificate is available and configured.
    449       if (!cert_loader_->IsHardwareBacked() || pkcs11_id.empty()) {
    450         ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired);
    451         return;
    452       }
    453     }
    454 
    455     // The network may not be 'Connectable' because the TPM properties are not
    456     // set up, so configure tpm slot/pin before connecting.
    457     if (cert_loader_ && cert_loader_->IsHardwareBacked()) {
    458       // Pass NULL if pkcs11_id is empty, so that it doesn't clear any
    459       // previously configured client cert.
    460       client_cert::SetShillProperties(client_cert_type,
    461                                       cert_loader_->tpm_token_slot(),
    462                                       cert_loader_->tpm_user_pin(),
    463                                       pkcs11_id.empty() ? NULL : &pkcs11_id,
    464                                       &config_properties);
    465     }
    466   }
    467 
    468   if (!config_properties.empty()) {
    469     NET_LOG_EVENT("Configuring Network", service_path);
    470 
    471     // Set configuration properties required by Shill to identify the network.
    472     config_properties.SetStringWithoutPathExpansion(
    473         flimflam::kTypeProperty, type);
    474     CopyStringFromDictionary(service_properties, flimflam::kNameProperty,
    475                              &config_properties);
    476     CopyStringFromDictionary(service_properties, flimflam::kGuidProperty,
    477                              &config_properties);
    478     if (type == flimflam::kTypeVPN) {
    479       config_properties.SetStringWithoutPathExpansion(
    480           flimflam::kProviderTypeProperty, vpn_provider_type);
    481       config_properties.SetStringWithoutPathExpansion(
    482           flimflam::kProviderHostProperty, vpn_provider_host);
    483     } else if (type == flimflam::kTypeWifi) {
    484       config_properties.SetStringWithoutPathExpansion(
    485           flimflam::kSecurityProperty, security);
    486     }
    487 
    488     network_configuration_handler_->SetProperties(
    489         service_path,
    490         config_properties,
    491         base::Bind(&NetworkConnectionHandler::CallShillConnect,
    492                    AsWeakPtr(), service_path),
    493         base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
    494                    AsWeakPtr(), service_path));
    495    return;
    496   }
    497 
    498   // Otherwise, we probably still need to configure the network since
    499   // 'Connectable' is false. If |check_error_state| is true, signal an
    500   // error, otherwise attempt to connect to possibly gain additional error
    501   // state from Shill (or in case 'Connectable' is improperly unset).
    502   if (check_error_state)
    503     ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired);
    504   else
    505     CallShillConnect(service_path);
    506 }
    507 
    508 void NetworkConnectionHandler::CallShillConnect(
    509     const std::string& service_path) {
    510   NET_LOG_EVENT("Sending Connect Request to Shill", service_path);
    511   DBusThreadManager::Get()->GetShillServiceClient()->Connect(
    512       dbus::ObjectPath(service_path),
    513       base::Bind(&NetworkConnectionHandler::HandleShillConnectSuccess,
    514                  AsWeakPtr(), service_path),
    515       base::Bind(&NetworkConnectionHandler::HandleShillConnectFailure,
    516                  AsWeakPtr(), service_path));
    517 }
    518 
    519 void NetworkConnectionHandler::HandleConfigurationFailure(
    520     const std::string& service_path,
    521     const std::string& error_name,
    522     scoped_ptr<base::DictionaryValue> error_data) {
    523   ConnectRequest* request = GetPendingRequest(service_path);
    524   if (!request) {
    525     NET_LOG_ERROR("HandleConfigurationFailure called with no pending request.",
    526                   service_path);
    527     return;
    528   }
    529   network_handler::ErrorCallback error_callback = request->error_callback;
    530   pending_requests_.erase(service_path);
    531   if (!error_callback.is_null())
    532     error_callback.Run(kErrorConfigureFailed, error_data.Pass());
    533 }
    534 
    535 void NetworkConnectionHandler::HandleShillConnectSuccess(
    536     const std::string& service_path) {
    537   ConnectRequest* request = GetPendingRequest(service_path);
    538   if (!request) {
    539     NET_LOG_ERROR("HandleShillConnectSuccess called with no pending request.",
    540                   service_path);
    541     return;
    542   }
    543   request->connect_state = ConnectRequest::CONNECT_STARTED;
    544   NET_LOG_EVENT("Connect Request Acknowledged", service_path);
    545   // Do not call success_callback here, wait for one of the following
    546   // conditions:
    547   // * State transitions to a non connecting state indicating succes or failure
    548   // * Network is no longer in the visible list, indicating failure
    549   CheckPendingRequest(service_path);
    550 }
    551 
    552 void NetworkConnectionHandler::HandleShillConnectFailure(
    553     const std::string& service_path,
    554     const std::string& dbus_error_name,
    555     const std::string& dbus_error_message) {
    556   ConnectRequest* request = GetPendingRequest(service_path);
    557   if (!request) {
    558     NET_LOG_ERROR("HandleShillConnectFailure called with no pending request.",
    559                   service_path);
    560     return;
    561   }
    562   network_handler::ErrorCallback error_callback = request->error_callback;
    563   pending_requests_.erase(service_path);
    564   network_handler::ShillErrorCallbackFunction(
    565       flimflam::kErrorConnectFailed, service_path, error_callback,
    566       dbus_error_name, dbus_error_message);
    567 }
    568 
    569 void NetworkConnectionHandler::CheckPendingRequest(
    570     const std::string service_path) {
    571   ConnectRequest* request = GetPendingRequest(service_path);
    572   DCHECK(request);
    573   if (request->connect_state == ConnectRequest::CONNECT_REQUESTED)
    574     return;  // Request has not started, ignore update
    575   const NetworkState* network =
    576       network_state_handler_->GetNetworkState(service_path);
    577   if (!network)
    578     return;  // NetworkState may not be be updated yet.
    579 
    580   if (network->IsConnectingState()) {
    581     request->connect_state = ConnectRequest::CONNECT_CONNECTING;
    582     return;
    583   }
    584   if (network->IsConnectedState()) {
    585     NET_LOG_EVENT("Connect Request Succeeded", service_path);
    586     if (!request->success_callback.is_null())
    587       request->success_callback.Run();
    588     pending_requests_.erase(service_path);
    589     return;
    590   }
    591   if (network->connection_state() == flimflam::kStateIdle &&
    592       request->connect_state != ConnectRequest::CONNECT_CONNECTING) {
    593     // Connection hasn't started yet, keep waiting.
    594     return;
    595   }
    596 
    597   // Network is neither connecting or connected; an error occurred.
    598   std::string error_name, error_detail;
    599   if (network->connection_state() == flimflam::kStateIdle &&
    600       pending_requests_.size() > 1) {
    601     // Another connect request canceled this one.
    602     error_name = kErrorConnectCanceled;
    603     error_detail = "";
    604   } else {
    605     error_name = flimflam::kErrorConnectFailed;
    606     error_detail = network->error();
    607     if (error_detail.empty()) {
    608       if (network->connection_state() == flimflam::kStateFailure)
    609         error_detail = flimflam::kUnknownString;
    610       else
    611         error_detail = "Unexpected State: " + network->connection_state();
    612     }
    613   }
    614   std::string error_msg = error_name + ": " + error_detail;
    615   NET_LOG_ERROR(error_msg, service_path);
    616 
    617   network_handler::ErrorCallback error_callback = request->error_callback;
    618   pending_requests_.erase(service_path);
    619   if (error_callback.is_null())
    620     return;
    621   scoped_ptr<base::DictionaryValue> error_data(
    622       network_handler::CreateErrorData(service_path, error_name, error_msg));
    623   error_callback.Run(error_name, error_data.Pass());
    624 }
    625 
    626 void NetworkConnectionHandler::CheckAllPendingRequests() {
    627   for (std::map<std::string, ConnectRequest>::iterator iter =
    628            pending_requests_.begin(); iter != pending_requests_.end(); ++iter) {
    629     CheckPendingRequest(iter->first);
    630   }
    631 }
    632 
    633 std::string NetworkConnectionHandler::CertificateIsConfigured(
    634     NetworkUIData* ui_data) {
    635   if (ui_data->certificate_pattern().Empty())
    636     return std::string();
    637   // Find the matching certificate.
    638   scoped_refptr<net::X509Certificate> matching_cert =
    639       client_cert::GetCertificateMatch(ui_data->certificate_pattern());
    640   if (!matching_cert.get())
    641     return std::string();
    642   return CertLoader::GetPkcs11IdForCert(*matching_cert.get());
    643 }
    644 
    645 void NetworkConnectionHandler::ErrorCallbackForPendingRequest(
    646     const std::string& service_path,
    647     const std::string& error_name) {
    648   ConnectRequest* request = GetPendingRequest(service_path);
    649   if (!request) {
    650     NET_LOG_ERROR("ErrorCallbackForPendingRequest with no pending request.",
    651                   service_path);
    652     return;
    653   }
    654   // Remove the entry before invoking the callback in case it triggers a retry.
    655   network_handler::ErrorCallback error_callback = request->error_callback;
    656   pending_requests_.erase(service_path);
    657   InvokeErrorCallback(service_path, error_callback, error_name);
    658 }
    659 
    660 // Disconnect
    661 
    662 void NetworkConnectionHandler::CallShillDisconnect(
    663     const std::string& service_path,
    664     const base::Closure& success_callback,
    665     const network_handler::ErrorCallback& error_callback) {
    666   NET_LOG_USER("Disconnect Request", service_path);
    667   DBusThreadManager::Get()->GetShillServiceClient()->Disconnect(
    668       dbus::ObjectPath(service_path),
    669       base::Bind(&NetworkConnectionHandler::HandleShillDisconnectSuccess,
    670                  AsWeakPtr(), service_path, success_callback),
    671       base::Bind(&network_handler::ShillErrorCallbackFunction,
    672                  kErrorShillError, service_path, error_callback));
    673 }
    674 
    675 void NetworkConnectionHandler::HandleShillDisconnectSuccess(
    676     const std::string& service_path,
    677     const base::Closure& success_callback) {
    678   NET_LOG_EVENT("Disconnect Request Sent", service_path);
    679   if (!success_callback.is_null())
    680     success_callback.Run();
    681 }
    682 
    683 // Activate
    684 
    685 void NetworkConnectionHandler::CallShillActivate(
    686     const std::string& service_path,
    687     const std::string& carrier,
    688     const base::Closure& success_callback,
    689     const network_handler::ErrorCallback& error_callback) {
    690   NET_LOG_USER("Activate Request", service_path + ": '" + carrier + "'");
    691   DBusThreadManager::Get()->GetShillServiceClient()->ActivateCellularModem(
    692       dbus::ObjectPath(service_path),
    693       carrier,
    694       base::Bind(&NetworkConnectionHandler::HandleShillActivateSuccess,
    695                  AsWeakPtr(), service_path, success_callback),
    696       base::Bind(&network_handler::ShillErrorCallbackFunction,
    697                  kErrorShillError, service_path, error_callback));
    698 }
    699 
    700 void NetworkConnectionHandler::HandleShillActivateSuccess(
    701     const std::string& service_path,
    702     const base::Closure& success_callback) {
    703   NET_LOG_EVENT("Activate Request Sent", service_path);
    704   if (!success_callback.is_null())
    705     success_callback.Run();
    706 }
    707 
    708 }  // namespace chromeos
    709