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