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