network_connection_handler.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/cert_loader.h"
15#include "chromeos/network/certificate_pattern_matcher.h"
16#include "chromeos/network/managed_network_configuration_handler.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_state.h"
21#include "chromeos/network/network_state_handler.h"
22#include "chromeos/network/network_ui_data.h"
23#include "dbus/object_path.h"
24#include "net/cert/x509_certificate.h"
25#include "third_party/cros_system_api/dbus/service_constants.h"
26
27namespace chromeos {
28
29namespace {
30
31void InvokeErrorCallback(const std::string& service_path,
32                         const network_handler::ErrorCallback& error_callback,
33                         const std::string& error_name) {
34  std::string error_msg = "Connect Error: " + error_name;
35  NET_LOG_ERROR(error_msg, service_path);
36  if (error_callback.is_null())
37    return;
38  scoped_ptr<base::DictionaryValue> error_data(
39      network_handler::CreateErrorData(service_path, error_name, error_msg));
40  error_callback.Run(error_name, error_data.Pass());
41}
42
43bool NetworkMayNeedCredentials(const NetworkState* network) {
44  if (network->type() == flimflam::kTypeWifi &&
45      (network->security() == flimflam::kSecurity8021x ||
46       network->security() == flimflam::kSecurityWep /* For dynamic WEP*/))
47    return true;
48  if (network->type() == flimflam::kTypeVPN)
49    return true;
50  return false;
51}
52
53bool NetworkRequiresActivation(const NetworkState* network) {
54  return (network->type() == flimflam::kTypeCellular &&
55          (network->activation_state() != flimflam::kActivationStateActivated ||
56           network->cellular_out_of_credits()));
57}
58
59bool VPNIsConfigured(const base::DictionaryValue& properties) {
60  std::string provider_type;
61  // Note: we use Value path expansion to extract Provider.Type.
62  properties.GetString(flimflam::kProviderTypeProperty, &provider_type);
63  if (provider_type == flimflam::kProviderOpenVpn) {
64    std::string hostname;
65    properties.GetString(flimflam::kProviderHostProperty, &hostname);
66    if (hostname.empty())
67      return false;
68    std::string username;
69    properties.GetStringWithoutPathExpansion(
70        flimflam::kOpenVPNUserProperty, &username);
71    if (username.empty())
72      return false;
73    std::string client_cert_id;
74    properties.GetStringWithoutPathExpansion(
75        flimflam::kOpenVPNClientCertIdProperty, &client_cert_id);
76    if (client_cert_id.empty())
77      return false;
78  } else {
79    bool passphrase_required = false;
80    std::string passphrase;
81    properties.GetBooleanWithoutPathExpansion(
82        flimflam::kL2tpIpsecPskRequiredProperty, &passphrase_required);
83    properties.GetStringWithoutPathExpansion(
84        flimflam::kL2tpIpsecPskProperty, &passphrase);
85    if (passphrase_required && passphrase.empty())
86      return false;
87  }
88  return true;
89}
90
91bool CertificateIsConfigured(NetworkUIData* ui_data) {
92  if (ui_data->certificate_type() != CLIENT_CERT_TYPE_PATTERN)
93    return true;  // No certificate or a reference.
94  if (ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY) {
95    // We skip checking certificate patterns for device policy ONC so that an
96    // unmanaged user can't get to the place where a cert is presented for them
97    // involuntarily.
98    return true;
99  }
100  if (ui_data->certificate_pattern().Empty())
101    return false;
102
103  // Find the matching certificate.
104  scoped_refptr<net::X509Certificate> matching_cert =
105      certificate_pattern::GetCertificateMatch(
106          ui_data->certificate_pattern());
107  if (!matching_cert.get())
108    return false;
109  return true;
110}
111
112}  // namespace
113
114const char NetworkConnectionHandler::kErrorNotFound[] = "not-found";
115const char NetworkConnectionHandler::kErrorConnected[] = "connected";
116const char NetworkConnectionHandler::kErrorConnecting[] = "connecting";
117const char NetworkConnectionHandler::kErrorNotConnected[] = "not-connected";
118const char NetworkConnectionHandler::kErrorPassphraseRequired[] =
119    "passphrase-required";
120const char NetworkConnectionHandler::kErrorActivationRequired[] =
121    "activation-required";
122const char NetworkConnectionHandler::kErrorCertificateRequired[] =
123    "certificate-required";
124const char NetworkConnectionHandler::kErrorConfigurationRequired[] =
125    "configuration-required";
126const char NetworkConnectionHandler::kErrorShillError[] = "shill-error";
127
128NetworkConnectionHandler::NetworkConnectionHandler()
129    : network_state_handler_(NULL),
130      network_configuration_handler_(NULL) {
131  const char* new_handlers_enabled =
132      CommandLine::ForCurrentProcess()->HasSwitch(
133          chromeos::switches::kUseNewNetworkConfigurationHandlers) ?
134      "enabled" : "disabled";
135  NET_LOG_EVENT("NewNetworkConfigurationHandlers", new_handlers_enabled);
136}
137
138NetworkConnectionHandler::~NetworkConnectionHandler() {
139}
140
141void NetworkConnectionHandler::Init(
142    NetworkStateHandler* network_state_handler,
143    NetworkConfigurationHandler* network_configuration_handler) {
144  network_state_handler_ = network_state_handler;
145  network_configuration_handler_ = network_configuration_handler;
146}
147
148void NetworkConnectionHandler::ConnectToNetwork(
149    const std::string& service_path,
150    const base::Closure& success_callback,
151    const network_handler::ErrorCallback& error_callback,
152    bool ignore_error_state) {
153  const NetworkState* network =
154      network_state_handler_->GetNetworkState(service_path);
155  if (!network) {
156    InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
157    return;
158  }
159  if (network->IsConnectedState()) {
160    InvokeErrorCallback(service_path, error_callback, kErrorConnected);
161    return;
162  }
163  if (network->IsConnectingState() ||
164      pending_requests_.find(service_path) != pending_requests_.end()) {
165    InvokeErrorCallback(service_path, error_callback, kErrorConnecting);
166    return;
167  }
168  if (NetworkRequiresActivation(network)) {
169    InvokeErrorCallback(service_path, error_callback, kErrorActivationRequired);
170    return;
171  }
172  if (!ignore_error_state) {
173    if (network->passphrase_required()) {
174      InvokeErrorCallback(service_path, error_callback,
175                          kErrorPassphraseRequired);
176      return;
177    }
178    if (network->error() == flimflam::kErrorConnectFailed) {
179      InvokeErrorCallback(service_path, error_callback,
180                          kErrorPassphraseRequired);
181      return;
182    }
183    if (network->error() == flimflam::kErrorBadPassphrase) {
184      InvokeErrorCallback(service_path, error_callback,
185                          kErrorPassphraseRequired);
186      return;
187    }
188    if (network->HasAuthenticationError()) {
189      InvokeErrorCallback(service_path, error_callback,
190                          kErrorConfigurationRequired);
191      return;
192    }
193  }
194
195  // All synchronous checks passed, add |service_path| to connecting list.
196  pending_requests_.insert(service_path);
197
198  if (!network->connectable() && NetworkMayNeedCredentials(network)) {
199    // Request additional properties to check.
200    network_configuration_handler_->GetProperties(
201        network->path(),
202        base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect,
203                   AsWeakPtr(), success_callback, error_callback),
204        base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure,
205                   AsWeakPtr(), network->path(), error_callback));
206    return;
207  }
208  // All checks passed, send connect request.
209  CallShillConnect(service_path, success_callback, error_callback);
210}
211
212void NetworkConnectionHandler::DisconnectNetwork(
213    const std::string& service_path,
214    const base::Closure& success_callback,
215    const network_handler::ErrorCallback& error_callback) {
216  const NetworkState* network =
217      network_state_handler_->GetNetworkState(service_path);
218  if (!network) {
219    InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
220    return;
221  }
222  if (!network->IsConnectedState()) {
223    InvokeErrorCallback(service_path, error_callback, kErrorNotConnected);
224    return;
225  }
226  CallShillDisconnect(service_path, success_callback, error_callback);
227}
228
229void NetworkConnectionHandler::CallShillConnect(
230    const std::string& service_path,
231    const base::Closure& success_callback,
232    const network_handler::ErrorCallback& error_callback) {
233  // TODO(stevenjb): Remove SetConnectingNetwork and use this class to maintain
234  // the connecting network(s) once NetworkLibrary path is eliminated.
235  network_state_handler_->SetConnectingNetwork(service_path);
236  NET_LOG_EVENT("Connect Request", service_path);
237  DBusThreadManager::Get()->GetShillServiceClient()->Connect(
238      dbus::ObjectPath(service_path),
239      base::Bind(&NetworkConnectionHandler::HandleShillSuccess,
240                 AsWeakPtr(), service_path, success_callback),
241      base::Bind(&NetworkConnectionHandler::HandleShillFailure,
242                 AsWeakPtr(), service_path, error_callback));
243}
244
245void NetworkConnectionHandler::CallShillDisconnect(
246    const std::string& service_path,
247    const base::Closure& success_callback,
248    const network_handler::ErrorCallback& error_callback) {
249  NET_LOG_EVENT("Disconnect Request", service_path);
250  DBusThreadManager::Get()->GetShillServiceClient()->Disconnect(
251      dbus::ObjectPath(service_path),
252      base::Bind(&NetworkConnectionHandler::HandleShillSuccess,
253                 AsWeakPtr(), service_path, success_callback),
254      base::Bind(&NetworkConnectionHandler::HandleShillFailure,
255                 AsWeakPtr(), service_path, error_callback));
256}
257
258void NetworkConnectionHandler::VerifyConfiguredAndConnect(
259    const base::Closure& success_callback,
260    const network_handler::ErrorCallback& error_callback,
261    const std::string& service_path,
262    const base::DictionaryValue& properties) {
263  const NetworkState* network =
264      network_state_handler_->GetNetworkState(service_path);
265  if (!network) {
266    InvokeErrorCallback(service_path, error_callback, kErrorNotFound);
267    return;
268  }
269
270  // VPN requires a host and username to be set.
271  if (network->type() == flimflam::kTypeVPN &&
272      !VPNIsConfigured(properties)) {
273    InvokeErrorCallback(service_path, error_callback,
274                        kErrorConfigurationRequired);
275    return;
276  }
277
278  // Check certificate properties in kUIDataProperty.
279  scoped_ptr<NetworkUIData> ui_data =
280      ManagedNetworkConfigurationHandler::GetUIData(properties);
281  if (ui_data && !CertificateIsConfigured(ui_data.get())) {
282    InvokeErrorCallback(service_path, error_callback,
283                        kErrorCertificateRequired);
284    return;
285  }
286
287  CallShillConnect(service_path, success_callback, error_callback);
288}
289
290void NetworkConnectionHandler::HandleConfigurationFailure(
291    const std::string& service_path,
292    const network_handler::ErrorCallback& error_callback,
293    const std::string& error_name,
294    scoped_ptr<base::DictionaryValue> error_data) {
295  pending_requests_.erase(service_path);
296  if (!error_callback.is_null())
297    error_callback.Run(error_name, error_data.Pass());
298}
299
300void NetworkConnectionHandler::HandleShillSuccess(
301    const std::string& service_path,
302    const base::Closure& success_callback) {
303  // TODO(stevenjb): Currently, this only indicates that the connect request
304  // succeeded. It might be preferable to wait for the actually connect
305  // attempt to succeed or fail here and only call |success_callback| at that
306  // point (or maybe call it twice, once indicating in-progress, then success
307  // or failure).
308  pending_requests_.erase(service_path);
309  NET_LOG_EVENT("Connect Request Sent", service_path);
310  success_callback.Run();
311}
312
313void NetworkConnectionHandler::HandleShillFailure(
314    const std::string& service_path,
315    const network_handler::ErrorCallback& error_callback,
316    const std::string& error_name,
317    const std::string& error_message) {
318  pending_requests_.erase(service_path);
319  std::string error = "Connect Failure: " + error_name + ": " + error_message;
320  network_handler::ShillErrorCallbackFunction(
321      service_path, error_callback, error_name, error_message);
322}
323
324}  // namespace chromeos
325