blob: 1b45bbcc35a6c1bc25ab4852da4f96c67b424821 [file] [log] [blame]
Darin Petkov33af05c2012-02-28 10:10:30 +01001// Copyright (c) 2012 The Chromium OS 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 "shill/openvpn_driver.h"
6
Darin Petkov14c29ec2012-03-02 11:34:19 +01007#include <arpa/inet.h>
8
Darin Petkov1fa81942012-04-02 11:38:08 +02009#include <base/file_util.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010010#include <base/string_number_conversions.h>
Darin Petkov78f63262012-03-26 01:30:24 +020011#include <base/string_split.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010012#include <base/string_util.h>
Darin Petkovfe6a9372012-02-28 16:25:06 +010013#include <chromeos/dbus/service_constants.h>
14
Paul Stewart5baebb72013-03-14 11:43:29 -070015#include "shill/certificate_file.h"
Paul Stewartce4ec192012-03-14 12:53:46 -070016#include "shill/connection.h"
Paul Stewartca6abd42012-03-01 15:45:29 -080017#include "shill/device_info.h"
Darin Petkov14c29ec2012-03-02 11:34:19 +010018#include "shill/dhcp_config.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010019#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070020#include "shill/logging.h"
Paul Stewartce4ec192012-03-14 12:53:46 -070021#include "shill/manager.h"
Darin Petkov3c5e4dc2012-04-02 14:44:27 +020022#include "shill/nss.h"
Darin Petkov46463022012-03-29 14:57:32 +020023#include "shill/openvpn_management_server.h"
Darin Petkov5a850472012-06-06 15:44:24 +020024#include "shill/process_killer.h"
Darin Petkova9b1fed2012-02-29 11:49:05 +010025#include "shill/rpc_task.h"
Darin Petkov46463022012-03-29 14:57:32 +020026#include "shill/sockets.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070027#include "shill/virtual_device.h"
Darin Petkov79d74c92012-03-07 17:20:32 +010028#include "shill/vpn_service.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010029
Darin Petkov5a850472012-06-06 15:44:24 +020030using base::Closure;
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080031using base::FilePath;
Darin Petkov78f63262012-03-26 01:30:24 +020032using base::SplitString;
Darin Petkova5e07ef2012-07-09 14:27:57 +020033using base::Unretained;
Darin Petkov5a850472012-06-06 15:44:24 +020034using base::WeakPtr;
Darin Petkov14c29ec2012-03-02 11:34:19 +010035using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010036using std::string;
37using std::vector;
38
Darin Petkov33af05c2012-02-28 10:10:30 +010039namespace shill {
40
Darin Petkova9b1fed2012-02-29 11:49:05 +010041namespace {
Darin Petkov5a850472012-06-06 15:44:24 +020042
Darin Petkov14c29ec2012-03-02 11:34:19 +010043const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
44const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
45const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
46const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
47const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
48const char kOpenVPNRouteOptionPrefix[] = "route_";
49const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
50const char kOpenVPNTrustedIP[] = "trusted_ip";
51const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010052
Darin Petkove0d5dd12012-04-04 16:10:48 +020053const char kDefaultPKCS11Provider[] = "libchaps.so";
54
Paul Stewart4698c1a2013-05-16 15:42:19 -070055// Some configurations pass the netmask in the ifconfig_remote property.
56// This is due to some servers not explicitly indicating that they are using
57// a "broadcast mode" network instead of peer-to-peer. See
58// http://crbug.com/241264 for an example of this issue.
59const char kSuspectedNetmaskPrefix[] = "255.";
60
Paul Stewartebd38562012-03-23 13:06:40 -070061} // namespace
62
63// static
Darin Petkovc418b4b2012-10-05 11:42:52 +020064const char OpenVPNDriver::kDefaultCACertificates[] =
65 "/etc/ssl/certs/ca-certificates.crt";
Darin Petkovca8a0e62012-09-26 13:16:52 +020066// static
Paul Stewartebd38562012-03-23 13:06:40 -070067const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
68// static
Darin Petkov728faa92012-10-12 11:25:47 +020069const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
Paul Stewartebd38562012-03-23 13:06:40 -070070// static
Darin Petkovd4325392012-04-23 15:48:22 +020071const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020072 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
73 { flimflam::kOpenVPNAuthProperty, 0 },
74 { flimflam::kOpenVPNAuthRetryProperty, 0 },
75 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
76 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
77 { flimflam::kOpenVPNCaCertProperty, 0 },
78 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020079 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020080 { flimflam::kOpenVPNCompLZOProperty, 0 },
81 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
82 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
83 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020084 { flimflam::kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020085 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
86 { flimflam::kOpenVPNPasswordProperty,
87 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020088 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020089 { flimflam::kOpenVPNPortProperty, 0 },
90 { flimflam::kOpenVPNProtoProperty, 0 },
91 { flimflam::kOpenVPNProviderProperty, 0 },
92 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
93 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
94 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
95 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
96 { flimflam::kOpenVPNRenegSecProperty, 0 },
97 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
98 { flimflam::kOpenVPNShaperProperty, 0 },
99 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
100 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
101 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
102 { flimflam::kOpenVPNUserProperty, 0 },
103 { flimflam::kProviderHostProperty, 0 },
Darin Petkov1847d792012-04-17 11:33:06 +0200104 { flimflam::kProviderTypeProperty, 0 },
Paul Stewart0f9c9302013-06-14 15:41:28 -0700105 { kOpenVPNCaCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200106 { kOpenVPNCertProperty, 0 },
Paul Stewartf3b6d572013-06-17 09:35:33 -0700107 { kOpenVPNExtraCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200108 { kOpenVPNKeyProperty, 0 },
109 { kOpenVPNPingExitProperty, 0 },
110 { kOpenVPNPingProperty, 0 },
111 { kOpenVPNPingRestartProperty, 0 },
112 { kOpenVPNTLSAuthProperty, 0 },
113 { kOpenVPNVerbProperty, 0 },
114 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700115
116 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200117 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100118};
Paul Stewart291a4732012-03-14 19:19:02 -0700119
Darin Petkov1a462de2012-05-02 11:10:48 +0200120const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
Darin Petkov1a462de2012-05-02 11:10:48 +0200121const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
Darin Petkov1a462de2012-05-02 11:10:48 +0200122const char OpenVPNDriver::kChromeOSReleaseVersion[] =
123 "CHROMEOS_RELEASE_VERSION";
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100124const int OpenVPNDriver::kReconnectOfflineTimeoutSeconds = 2 * 60;
125const int OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds = 20;
Darin Petkov1a462de2012-05-02 11:10:48 +0200126
Darin Petkova9b1fed2012-02-29 11:49:05 +0100127OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100128 EventDispatcher *dispatcher,
129 Metrics *metrics,
130 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800131 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700132 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +0200133 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200134 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100135 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800136 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100137 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200138 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200139 nss_(NSS::GetInstance()),
Paul Stewarteb713e82013-06-28 14:51:54 -0700140 certificate_file_(new CertificateFile()),
Darin Petkov5a850472012-06-06 15:44:24 +0200141 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200142 lsb_release_file_(kLSBReleaseFile),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100143 pid_(0),
Darin Petkova5e07ef2012-07-09 14:27:57 +0200144 child_watch_tag_(0),
145 default_service_callback_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100146
Darin Petkov36a3ace2012-03-06 17:22:14 +0100147OpenVPNDriver::~OpenVPNDriver() {
Darin Petkovaba89322013-03-11 14:48:22 +0100148 IdleService();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100149}
150
Darin Petkovaba89322013-03-11 14:48:22 +0100151void OpenVPNDriver::IdleService() {
Darin Petkov1c049c72013-03-21 13:15:45 +0100152 Cleanup(Service::kStateIdle,
153 Service::kFailureUnknown,
154 Service::kErrorDetailsNone);
Darin Petkovaba89322013-03-11 14:48:22 +0100155}
156
Darin Petkov1c049c72013-03-21 13:15:45 +0100157void OpenVPNDriver::FailService(Service::ConnectFailure failure,
158 const string &error_details) {
159 Cleanup(Service::kStateFailure, failure, error_details);
Darin Petkovaba89322013-03-11 14:48:22 +0100160}
161
162void OpenVPNDriver::Cleanup(Service::ConnectState state,
Darin Petkov1c049c72013-03-21 13:15:45 +0100163 Service::ConnectFailure failure,
Darin Petkovaba89322013-03-11 14:48:22 +0100164 const string &error_details) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700165 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
Darin Petkovaba89322013-03-11 14:48:22 +0100166 << ", " << error_details << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200167 StopConnectTimeout();
Darin Petkov84a95532013-03-13 12:24:45 +0100168 if (child_watch_tag_) {
169 glib_->SourceRemove(child_watch_tag_);
170 child_watch_tag_ = 0;
171 }
172 // Disconnecting the management interface will terminate the openvpn
173 // process. Ensure this is handled robustly by first removing the child watch
174 // above and then terminating and reaping the process through ProcessKiller.
Darin Petkov46463022012-03-29 14:57:32 +0200175 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200176 if (!tls_auth_file_.empty()) {
177 file_util::Delete(tls_auth_file_, false);
178 tls_auth_file_.clear();
179 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200180 if (default_service_callback_tag_) {
181 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
182 default_service_callback_tag_ = 0;
183 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100184 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200185 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100186 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200187 interface_index = device_->interface_index();
mukesh agrawal9da07772013-05-15 14:15:17 -0700188 device_->DropConnection();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500189 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100190 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200191 }
192 if (pid_) {
193 Closure callback;
194 if (interface_index >= 0) {
195 callback =
196 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
197 interface_index = -1;
198 }
199 process_killer_->Kill(pid_, callback);
200 pid_ = 0;
201 }
202 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100203 device_info_->DeleteInterface(interface_index);
204 }
205 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100206 if (service_) {
Darin Petkov1c049c72013-03-21 13:15:45 +0100207 if (state == Service::kStateFailure) {
208 service_->SetErrorDetails(error_details);
209 service_->SetFailure(failure);
210 } else {
211 service_->SetState(state);
212 }
Darin Petkov79d74c92012-03-07 17:20:32 +0100213 service_ = NULL;
214 }
Darin Petkov3189a472012-10-05 09:55:33 +0200215 ip_properties_ = IPConfig::Properties();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100216}
217
218bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700219 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100220
221 vector<string> options;
222 Error error;
223 InitOptions(&options, &error);
224 if (error.IsFailure()) {
225 return false;
226 }
Darin Petkov728faa92012-10-12 11:25:47 +0200227 LOG(INFO) << "OpenVPN process options: " << JoinString(options, ' ');
Darin Petkov36a3ace2012-03-06 17:22:14 +0100228
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700229 // TODO(quiche): This should be migrated to use ExternalTask.
230 // (crbug.com/246263).
Darin Petkov36a3ace2012-03-06 17:22:14 +0100231 vector<char *> process_args;
232 process_args.push_back(const_cast<char *>(kOpenVPNPath));
233 for (vector<string>::const_iterator it = options.begin();
234 it != options.end(); ++it) {
235 process_args.push_back(const_cast<char *>(it->c_str()));
236 }
237 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200238
239 vector<string> environment;
240 InitEnvironment(&environment);
241
242 vector<char *> process_env;
243 for (vector<string>::const_iterator it = environment.begin();
244 it != environment.end(); ++it) {
245 process_env.push_back(const_cast<char *>(it->c_str()));
246 }
247 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100248
249 CHECK(!pid_);
Darin Petkov68710d72013-02-13 14:22:56 +0100250 if (!glib_->SpawnAsync(NULL,
251 process_args.data(),
252 process_env.data(),
253 G_SPAWN_DO_NOT_REAP_CHILD,
254 NULL,
255 NULL,
256 &pid_,
257 NULL)) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100258 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
259 return false;
260 }
261 CHECK(!child_watch_tag_);
262 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
263 return true;
264}
265
266// static
267void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700268 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100269 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
270 me->child_watch_tag_ = 0;
271 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200272 me->pid_ = 0;
Darin Petkov1c049c72013-03-21 13:15:45 +0100273 me->FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100274 // TODO(petkov): Figure if we need to restart the connection.
275}
Darin Petkov33af05c2012-02-28 10:10:30 +0100276
Darin Petkov5a850472012-06-06 15:44:24 +0200277// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200278void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200279 int interface_index) {
280 if (device_info) {
281 LOG(INFO) << "Deleting interface " << interface_index;
282 device_info->DeleteInterface(interface_index);
283 }
284}
285
Paul Stewartca6abd42012-03-01 15:45:29 -0800286bool OpenVPNDriver::ClaimInterface(const string &link_name,
287 int interface_index) {
288 if (link_name != tunnel_interface_) {
289 return false;
290 }
291
Ben Chanfad4a0b2012-04-18 15:49:59 -0700292 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800293
Darin Petkovf20994f2012-03-05 16:12:19 +0100294 CHECK(!device_);
mukesh agrawal9da07772013-05-15 14:15:17 -0700295 device_ = new VirtualDevice(control_, dispatcher(), metrics_, manager(),
296 link_name, interface_index, Technology::kVPN);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500297 device_->SetEnabled(true);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500298
Darin Petkov36a3ace2012-03-06 17:22:14 +0100299 rpc_task_.reset(new RPCTask(control_, this));
Darin Petkov1c049c72013-03-21 13:15:45 +0100300 if (SpawnOpenVPN()) {
Darin Petkov68710d72013-02-13 14:22:56 +0100301 default_service_callback_tag_ =
302 manager()->RegisterDefaultServiceCallback(
303 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Darin Petkov1c049c72013-03-21 13:15:45 +0100304 } else {
305 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100306 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800307 return true;
308}
309
Darin Petkov209e6292012-04-20 11:33:32 +0200310void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
311 NOTREACHED();
312}
313
Darin Petkov36a3ace2012-03-06 17:22:14 +0100314void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100315 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200316 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100317 if (reason != "up") {
mukesh agrawal9da07772013-05-15 14:15:17 -0700318 device_->DropConnection();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100319 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100320 }
Darin Petkov3189a472012-10-05 09:55:33 +0200321 // On restart/reconnect, update the existing IP configuration.
322 ParseIPConfiguration(dict, &ip_properties_);
323 device_->SelectService(service_);
324 device_->UpdateIPConfig(ip_properties_);
Paul Stewart91a43cb2013-03-02 21:34:15 -0800325 ReportConnectionMetrics();
Darin Petkov602303f2012-06-06 12:15:59 +0200326 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100327}
328
329// static
330void OpenVPNDriver::ParseIPConfiguration(
331 const map<string, string> &configuration,
332 IPConfig::Properties *properties) {
333 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100334 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100335 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200336 if (!properties->subnet_prefix) {
337 properties->subnet_prefix =
338 IPAddress::GetMaxPrefixLength(properties->address_family);
339 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100340 for (map<string, string>::const_iterator it = configuration.begin();
341 it != configuration.end(); ++it) {
342 const string &key = it->first;
343 const string &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700344 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100345 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
346 properties->address = value;
347 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
348 properties->broadcast_address = value;
349 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700350 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100351 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
352 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
Paul Stewart4698c1a2013-05-16 15:42:19 -0700353 if (StartsWithASCII(value, kSuspectedNetmaskPrefix, false)) {
354 LOG(WARNING) << "Option " << key << " value " << value
355 << " looks more like a netmask than a peer address; "
356 << "assuming it is the former.";
357 // In this situation, the "peer_address" value will be left
358 // unset and Connection::UpdateFromIPConfig() will treat the
359 // interface as if it were a broadcast-style network. The
360 // kernel will, automatically set the peer address equal to
361 // the local address.
362 properties->subnet_prefix =
363 IPAddress::GetPrefixLengthFromMask(properties->address_family,
364 value);
365 } else {
366 properties->peer_address = value;
367 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100368 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
369 properties->gateway = value;
370 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700371 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100372 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
373 int mtu = 0;
374 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
375 properties->mtu = mtu;
376 } else {
377 LOG(ERROR) << "MTU " << value << " ignored.";
378 }
379 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
380 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
381 int order = 0;
382 if (base::StringToInt(suffix, &order)) {
383 foreign_options[order] = value;
384 } else {
385 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
386 }
387 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100388 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
389 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100390 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700391 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100392 }
393 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100394 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100395 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100396}
397
398// static
399void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
400 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200401 vector<string> domain_search;
402 vector<string> dns_servers;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100403 for (ForeignOptions::const_iterator it = options.begin();
404 it != options.end(); ++it) {
Darin Petkove8587e32012-07-02 13:56:07 +0200405 ParseForeignOption(it->second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100406 }
Darin Petkove8587e32012-07-02 13:56:07 +0200407 if (!domain_search.empty()) {
408 properties->domain_search.swap(domain_search);
409 }
410 LOG_IF(WARNING, properties->domain_search.empty())
411 << "No search domains provided.";
412 if (!dns_servers.empty()) {
413 properties->dns_servers.swap(dns_servers);
414 }
415 LOG_IF(WARNING, properties->dns_servers.empty())
416 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100417}
418
419// static
420void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200421 vector<string> *domain_search,
422 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700423 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100424 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200425 SplitString(option, ' ', &tokens);
426 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100427 return;
428 }
429 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200430 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100431 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200432 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100433 }
434}
435
Darin Petkov60596742012-03-05 12:17:17 +0100436// static
437IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
438 const string &prefix, const string &key, RouteOptions *routes) {
439 int order = 0;
440 if (!StartsWithASCII(key, prefix, false) ||
441 !base::StringToInt(key.substr(prefix.size()), &order)) {
442 return NULL;
443 }
444 return &(*routes)[order];
445}
446
447// static
448void OpenVPNDriver::ParseRouteOption(
449 const string &key, const string &value, RouteOptions *routes) {
450 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
451 if (route) {
452 route->host = value;
453 return;
454 }
455 route = GetRouteOptionEntry("netmask_", key, routes);
456 if (route) {
457 route->netmask = value;
458 return;
459 }
460 route = GetRouteOptionEntry("gateway_", key, routes);
461 if (route) {
462 route->gateway = value;
463 return;
464 }
465 LOG(WARNING) << "Unknown route option ignored: " << key;
466}
467
468// static
469void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
470 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200471 vector<IPConfig::Route> new_routes;
Darin Petkov60596742012-03-05 12:17:17 +0100472 for (RouteOptions::const_iterator it = routes.begin();
473 it != routes.end(); ++it) {
474 const IPConfig::Route &route = it->second;
475 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
476 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
477 continue;
478 }
Darin Petkove8587e32012-07-02 13:56:07 +0200479 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100480 }
Darin Petkove8587e32012-07-02 13:56:07 +0200481 if (!new_routes.empty()) {
482 properties->routes.swap(new_routes);
483 }
484 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100485}
486
Darin Petkov4b944842012-09-21 10:48:48 +0200487// static
488bool OpenVPNDriver::SplitPortFromHost(
489 const string &host, string *name, string *port) {
490 vector<string> tokens;
491 SplitString(host, ':', &tokens);
492 int port_number = 0;
493 if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
494 !IsAsciiDigit(tokens[1][0]) ||
495 !base::StringToInt(tokens[1], &port_number) || port_number > kuint16max) {
496 return false;
497 }
498 *name = tokens[0];
499 *port = tokens[1];
500 return true;
501}
502
Darin Petkov602303f2012-06-06 12:15:59 +0200503void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100504 StartConnectTimeout(kDefaultConnectTimeoutSeconds);
Darin Petkov79d74c92012-03-07 17:20:32 +0100505 service_ = service;
506 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100507 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
508 Error::PopulateAndLog(
509 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov1c049c72013-03-21 13:15:45 +0100510 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkovf20994f2012-03-05 16:12:19 +0100511 }
512 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100513}
514
Darin Petkovfe6a9372012-02-28 16:25:06 +0100515void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200516 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100517 if (vpnhost.empty()) {
518 Error::PopulateAndLog(
519 error, Error::kInvalidArguments, "VPN host not specified.");
520 return;
521 }
522 options->push_back("--client");
523 options->push_back("--tls-client");
Darin Petkov4b944842012-09-21 10:48:48 +0200524
Darin Petkovfe6a9372012-02-28 16:25:06 +0100525 options->push_back("--remote");
Darin Petkov4b944842012-09-21 10:48:48 +0200526 string host_name, host_port;
527 if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
528 DCHECK(!host_name.empty());
529 DCHECK(!host_port.empty());
530 options->push_back(host_name);
531 options->push_back(host_port);
532 } else {
533 options->push_back(vpnhost);
534 }
535
Darin Petkovfe6a9372012-02-28 16:25:06 +0100536 options->push_back("--nobind");
537 options->push_back("--persist-key");
538 options->push_back("--persist-tun");
539
Darin Petkovf20994f2012-03-05 16:12:19 +0100540 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800541 options->push_back("--dev");
542 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100543 options->push_back("--dev-type");
544 options->push_back("tun");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100545
Darin Petkov55771b72012-04-25 09:25:19 +0200546 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100547
Darin Petkovf3c71d72012-03-21 12:32:15 +0100548 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100549 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
550 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100551 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200552 {
553 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200554 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200555 if (!contents.empty()) {
556 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
557 file_util::WriteFile(
558 tls_auth_file_, contents.data(), contents.size()) !=
559 static_cast<int>(contents.size())) {
560 Error::PopulateAndLog(
561 error, Error::kInternalError, "Unable to setup tls-auth file.");
562 return;
563 }
564 options->push_back("--tls-auth");
565 options->push_back(tls_auth_file_.value());
566 }
567 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100568 AppendValueOption(
569 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
570 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
571 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
572 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
573 AppendValueOption(
574 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
575 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
576 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
577 AppendFlag(
578 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
579 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
580 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
581 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
582 "--server-poll-timeout", options);
583
Darin Petkovca8a0e62012-09-26 13:16:52 +0200584 if (!InitCAOptions(options, error)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200585 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200586 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100587
588 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100589 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
590 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
591 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100592
Darin Petkovfe6a9372012-02-28 16:25:06 +0100593 AppendValueOption(
594 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100595
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200596 InitClientAuthOptions(options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200597 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100598
599 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100600 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200601 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100602 if (remote_cert_tls.empty()) {
603 remote_cert_tls = "server";
604 }
605 if (remote_cert_tls != "none") {
606 options->push_back("--remote-cert-tls");
607 options->push_back(remote_cert_tls);
608 }
609
610 // This is an undocumented command line argument that works like a .cfg file
611 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
612 AppendValueOption(
613 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
614 // TODO(sleffler): Support more than one eku parameter.
615 AppendValueOption(
616 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
617 AppendValueOption(
618 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
619
Darin Petkove0d5dd12012-04-04 16:10:48 +0200620 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200621 return;
622 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100623
Darin Petkova9b1fed2012-02-29 11:49:05 +0100624 // Setup openvpn-script options and RPC information required to send back
625 // Layer 3 configuration.
626 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200627 options->push_back(kRPCTaskServiceVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100628 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
629 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200630 options->push_back(kRPCTaskPathVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100631 options->push_back(rpc_task_->GetRpcIdentifier());
632 options->push_back("--script-security");
633 options->push_back("2");
634 options->push_back("--up");
635 options->push_back(kOpenVPNScript);
636 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100637
638 // Disable openvpn handling since we do route+ifconfig work.
639 options->push_back("--route-noexec");
640 options->push_back("--ifconfig-noexec");
641
642 // Drop root privileges on connection and enable callback scripts to send
643 // notify messages.
644 options->push_back("--user");
645 options->push_back("openvpn");
646 options->push_back("--group");
647 options->push_back("openvpn");
648}
649
Darin Petkovca8a0e62012-09-26 13:16:52 +0200650bool OpenVPNDriver::InitCAOptions(vector<string> *options, Error *error) {
Darin Petkovc418b4b2012-10-05 11:42:52 +0200651 options->push_back("--ca");
Darin Petkovb451d6e2012-04-23 11:56:41 +0200652 string ca_cert =
Darin Petkovca8a0e62012-09-26 13:16:52 +0200653 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "");
654 string ca_cert_nss =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200655 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Paul Stewart0f9c9302013-06-14 15:41:28 -0700656 vector<string> ca_cert_pem;
657 if (args()->ContainsStrings(kOpenVPNCaCertPemProperty)) {
658 ca_cert_pem = args()->GetStrings(kOpenVPNCaCertPemProperty);
659 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700660
661 int num_ca_cert_types = 0;
662 if (!ca_cert.empty())
663 num_ca_cert_types++;
664 if (!ca_cert_nss.empty())
665 num_ca_cert_types++;
666 if (!ca_cert_pem.empty())
667 num_ca_cert_types++;
668 if (num_ca_cert_types == 0) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200669 // Use default CAs if no CA certificate is provided.
Darin Petkovc418b4b2012-10-05 11:42:52 +0200670 options->push_back(kDefaultCACertificates);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200671 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700672 } else if (num_ca_cert_types > 1) {
673 Error::PopulateAndLog(
674 error, Error::kInvalidArguments,
675 "Can't specify more than one of CACert, CACertNSS and CACertPEM.");
Darin Petkovca8a0e62012-09-26 13:16:52 +0200676 return false;
677 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700678 string cert_file;
Darin Petkovca8a0e62012-09-26 13:16:52 +0200679 if (!ca_cert_nss.empty()) {
Paul Stewart5baebb72013-03-14 11:43:29 -0700680 DCHECK(ca_cert.empty() && ca_cert_pem.empty());
Darin Petkovb451d6e2012-04-23 11:56:41 +0200681 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200682 vector<char> id(vpnhost.begin(), vpnhost.end());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200683 FilePath certfile = nss_->GetPEMCertfile(ca_cert_nss, id);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200684 if (certfile.empty()) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200685 Error::PopulateAndLog(
686 error,
687 Error::kInvalidArguments,
688 "Unable to extract NSS CA certificate: " + ca_cert_nss);
689 return false;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200690 }
Darin Petkovca8a0e62012-09-26 13:16:52 +0200691 options->push_back(certfile.value());
692 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700693 } else if (!ca_cert_pem.empty()) {
694 DCHECK(ca_cert.empty() && ca_cert_nss.empty());
Paul Stewart0f9c9302013-06-14 15:41:28 -0700695 FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_cert_pem);
Paul Stewart5baebb72013-03-14 11:43:29 -0700696 if (certfile.empty()) {
697 Error::PopulateAndLog(
698 error,
699 Error::kInvalidArguments,
Paul Stewart0f9c9302013-06-14 15:41:28 -0700700 "Unable to extract PEM CA certificates.");
Paul Stewart5baebb72013-03-14 11:43:29 -0700701 return false;
702 }
703 options->push_back(certfile.value());
704 return true;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200705 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700706 DCHECK(!ca_cert.empty() && ca_cert_nss.empty() && ca_cert_pem.empty());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200707 options->push_back(ca_cert);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200708 return true;
709}
710
711void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200712 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200713 if (!id.empty()) {
714 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200715 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200716 if (provider.empty()) {
717 provider = kDefaultPKCS11Provider;
718 }
719 options->push_back("--pkcs11-providers");
720 options->push_back(provider);
721 options->push_back("--pkcs11-id");
722 options->push_back(id);
723 }
724}
725
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200726void OpenVPNDriver::InitClientAuthOptions(vector<string> *options) {
Paul Stewartbb985102013-06-27 16:51:11 -0700727 bool has_cert = AppendValueOption(kOpenVPNCertProperty, "--cert", options) ||
728 !args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "").empty();
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200729 bool has_key = AppendValueOption(kOpenVPNKeyProperty, "--key", options);
730 // If the AuthUserPass property is set, or the User property is non-empty, or
731 // there's neither a key, nor a cert available, specify user-password client
732 // authentication.
733 if (args()->ContainsString(flimflam::kOpenVPNAuthUserPassProperty) ||
734 !args()->LookupString(flimflam::kOpenVPNUserProperty, "").empty() ||
735 (!has_cert && !has_key)) {
736 options->push_back("--auth-user-pass");
737 }
738}
739
Darin Petkove0d5dd12012-04-04 16:10:48 +0200740bool OpenVPNDriver::InitManagementChannelOptions(
741 vector<string> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200742 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200743 Error::PopulateAndLog(
744 error, Error::kInternalError, "Unable to setup management channel.");
745 return false;
746 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200747 // If there's a connected default service already, allow the openvpn client to
748 // establish connection as soon as it's started. Otherwise, hold the client
749 // until an underlying service connects and OnDefaultServiceChanged is
750 // invoked.
Darin Petkov4cbff5b2013-01-29 16:29:05 +0100751 if (manager()->IsOnline()) {
Darin Petkova5e07ef2012-07-09 14:27:57 +0200752 management_server_->ReleaseHold();
753 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200754 return true;
755}
756
Darin Petkov55771b72012-04-25 09:25:19 +0200757void OpenVPNDriver::InitLoggingOptions(vector<string> *options) {
758 options->push_back("--syslog");
759
760 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
761 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
762 verb = "3";
763 }
764 if (!verb.empty()) {
765 options->push_back("--verb");
766 options->push_back(verb);
767 }
768}
769
Darin Petkov46463022012-03-29 14:57:32 +0200770bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100771 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200772 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100773 if (!value.empty()) {
774 options->push_back(option);
775 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200776 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100777 }
Darin Petkov46463022012-03-29 14:57:32 +0200778 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100779}
780
Darin Petkov46463022012-03-29 14:57:32 +0200781bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100782 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200783 if (args()->ContainsString(property)) {
Darin Petkovfe6a9372012-02-28 16:25:06 +0100784 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200785 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100786 }
Darin Petkov46463022012-03-29 14:57:32 +0200787 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100788}
789
Darin Petkov6aa21872012-03-09 16:10:19 +0100790void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700791 SLOG(VPN, 2) << __func__;
Darin Petkovaba89322013-03-11 14:48:22 +0100792 IdleService();
Darin Petkov6aa21872012-03-09 16:10:19 +0100793}
794
Darin Petkov5eb05422012-05-11 15:45:25 +0200795void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkova42afe32013-02-05 16:53:52 +0100796 LOG(INFO) << "Underlying connection disconnected.";
797 // Restart the OpenVPN client forcing a reconnect attempt.
798 management_server_->Restart();
799 // Indicate reconnect state right away to drop the VPN connection and start
800 // the connect timeout. This ensures that any miscommunication between shill
801 // and openvpn will not lead to a permanently stale connectivity state. Note
802 // that a subsequent invocation of OnReconnecting due to a RECONNECTING
803 // message will essentially be a no-op.
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100804 OnReconnecting(kReconnectReasonOffline);
Darin Petkova42afe32013-02-05 16:53:52 +0100805}
806
807void OpenVPNDriver::OnConnectTimeout() {
808 VPNDriver::OnConnectTimeout();
Darin Petkov1c049c72013-03-21 13:15:45 +0100809 Service::ConnectFailure failure =
810 management_server_->state() == OpenVPNManagementServer::kStateResolve ?
811 Service::kFailureDNSLookup : Service::kFailureConnect;
812 FailService(failure, Service::kErrorDetailsNone);
Darin Petkov5eb05422012-05-11 15:45:25 +0200813}
814
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100815void OpenVPNDriver::OnReconnecting(ReconnectReason reason) {
816 LOG(INFO) << __func__ << "(" << reason << ")";
817 int timeout_seconds = GetReconnectTimeoutSeconds(reason);
818 if (reason == kReconnectReasonTLSError &&
819 timeout_seconds < connect_timeout_seconds()) {
820 // Reconnect due to TLS error happens during connect so we need to cancel
821 // the original connect timeout first and then reduce the time limit.
822 StopConnectTimeout();
823 }
824 StartConnectTimeout(timeout_seconds);
Darin Petkova5e07ef2012-07-09 14:27:57 +0200825 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
826 // might be in hold state if the VPN connection was previously established
827 // successfully. The hold will be released by OnDefaultServiceChanged when a
828 // new default service connects. This ensures that the client will use a fully
829 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200830 if (device_) {
mukesh agrawal9da07772013-05-15 14:15:17 -0700831 device_->DropConnection();
Darin Petkov271fe522012-03-27 13:47:29 +0200832 }
833 if (service_) {
834 service_->SetState(Service::kStateAssociating);
835 }
836}
837
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100838// static
839int OpenVPNDriver::GetReconnectTimeoutSeconds(ReconnectReason reason) {
840 switch (reason) {
841 case kReconnectReasonOffline:
842 return kReconnectOfflineTimeoutSeconds;
843 case kReconnectReasonTLSError:
844 return kReconnectTLSErrorTimeoutSeconds;
845 default:
846 break;
847 }
848 return kDefaultConnectTimeoutSeconds;
849}
850
Paul Stewart39964fa2012-04-04 09:50:25 -0700851string OpenVPNDriver::GetProviderType() const {
852 return flimflam::kProviderOpenVpn;
853}
854
Darin Petkovb536a742012-04-26 11:31:28 +0200855KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
856 SLOG(VPN, 2) << __func__;
857 KeyValueStore props = VPNDriver::GetProvider(error);
858 props.SetBool(flimflam::kPassphraseRequiredProperty,
859 args()->LookupString(
860 flimflam::kOpenVPNPasswordProperty, "").empty());
861 return props;
862}
863
Darin Petkov1a462de2012-05-02 11:10:48 +0200864// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
865// singleton if it's used outside OpenVPN.
866bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
867 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
868 string contents;
869 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
870 LOG(ERROR) << "Unable to read the lsb-release file: "
871 << lsb_release_file_.value();
872 return false;
873 }
874 vector<string> lines;
875 SplitString(contents, '\n', &lines);
876 for (vector<string>::const_iterator it = lines.begin();
877 it != lines.end(); ++it) {
878 size_t assign = it->find('=');
879 if (assign == string::npos) {
880 continue;
881 }
882 (*lsb_release)[it->substr(0, assign)] = it->substr(assign + 1);
883 }
884 return true;
885}
886
887void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
888 // Adds the platform name and version to the environment so that openvpn can
889 // send them to the server when OpenVPN.PushPeerInfo is set.
890 map<string, string> lsb_release;
891 ParseLSBRelease(&lsb_release);
892 string platform_name = lsb_release[kChromeOSReleaseName];
893 if (!platform_name.empty()) {
894 environment->push_back("IV_PLAT=" + platform_name);
895 }
896 string platform_version = lsb_release[kChromeOSReleaseVersion];
897 if (!platform_version.empty()) {
898 environment->push_back("IV_PLAT_REL=" + platform_version);
899 }
900}
901
Darin Petkova5e07ef2012-07-09 14:27:57 +0200902void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
903 SLOG(VPN, 2) << __func__
Darin Petkov457728b2013-01-09 09:49:08 +0100904 << "(" << (service ? service->unique_name() : "-") << ")";
Darin Petkova5e07ef2012-07-09 14:27:57 +0200905 // Allow the openvpn client to connect/reconnect only over a connected
906 // underlying default service. If there's no default connected service, hold
907 // the openvpn client until an underlying connection is established. If the
908 // default service is our VPN service, hold the openvpn client on reconnect so
909 // that the VPN connection can be torn down fully before a new connection
910 // attempt is made over the underlying service.
911 if (service && service != service_ && service->IsConnected()) {
912 management_server_->ReleaseHold();
913 } else {
914 management_server_->Hold();
915 }
916}
917
Paul Stewart91a43cb2013-03-02 21:34:15 -0800918void OpenVPNDriver::ReportConnectionMetrics() {
919 metrics_->SendEnumToUMA(
920 Metrics::kMetricVpnDriver,
921 Metrics::kVpnDriverOpenVpn,
922 Metrics::kMetricVpnDriverMax);
923
Paul Stewarte8e71da2013-03-20 08:48:33 -0700924 if (args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "") != "" ||
925 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800926 metrics_->SendEnumToUMA(
927 Metrics::kMetricVpnRemoteAuthenticationType,
928 Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
929 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
930 } else {
931 metrics_->SendEnumToUMA(
932 Metrics::kMetricVpnRemoteAuthenticationType,
933 Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
934 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
935 }
936
937 bool has_user_authentication = false;
Paul Stewarte8e71da2013-03-20 08:48:33 -0700938 if (args()->LookupString(flimflam::kOpenVPNOTPProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800939 metrics_->SendEnumToUMA(
940 Metrics::kMetricVpnUserAuthenticationType,
941 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
942 Metrics::kMetricVpnUserAuthenticationTypeMax);
943 has_user_authentication = true;
944 }
Paul Stewarte8e71da2013-03-20 08:48:33 -0700945 if (args()->LookupString(flimflam::kOpenVPNAuthUserPassProperty, "") != ""||
946 args()->LookupString(flimflam::kOpenVPNUserProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800947 metrics_->SendEnumToUMA(
948 Metrics::kMetricVpnUserAuthenticationType,
949 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
950 Metrics::kMetricVpnUserAuthenticationTypeMax);
951 has_user_authentication = true;
952 }
Paul Stewarte8e71da2013-03-20 08:48:33 -0700953 if (args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "") != "" ||
954 args()->LookupString(kOpenVPNCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800955 metrics_->SendEnumToUMA(
956 Metrics::kMetricVpnUserAuthenticationType,
957 Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
958 Metrics::kMetricVpnUserAuthenticationTypeMax);
959 has_user_authentication = true;
960 }
961 if (!has_user_authentication) {
962 metrics_->SendEnumToUMA(
963 Metrics::kMetricVpnUserAuthenticationType,
964 Metrics::kVpnUserAuthenticationTypeOpenVpnNone,
965 Metrics::kMetricVpnUserAuthenticationTypeMax);
966 }
967}
968
Darin Petkov33af05c2012-02-28 10:10:30 +0100969} // namespace shill