blob: bdb186f547bc5bff5641f76d9b68a3476ecb5707 [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"
Darin Petkovf20994f2012-03-05 16:12:19 +010027#include "shill/vpn.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
Darin Petkovf3c71d72012-03-21 12:32:15 +010055// TODO(petkov): Move to chromeos/dbus/service_constants.h.
Darin Petkovf3c71d72012-03-21 12:32:15 +010056const char kOpenVPNPingProperty[] = "OpenVPN.Ping";
57const char kOpenVPNPingExitProperty[] = "OpenVPN.PingExit";
58const char kOpenVPNPingRestartProperty[] = "OpenVPN.PingRestart";
59const char kOpenVPNTLSAuthProperty[] = "OpenVPN.TLSAuth";
60const char kOpenVPNVerbProperty[] = "OpenVPN.Verb";
61const char kVPNMTUProperty[] = "VPN.MTU";
Darin Petkov5a850472012-06-06 15:44:24 +020062
Paul Stewart4698c1a2013-05-16 15:42:19 -070063// Some configurations pass the netmask in the ifconfig_remote property.
64// This is due to some servers not explicitly indicating that they are using
65// a "broadcast mode" network instead of peer-to-peer. See
66// http://crbug.com/241264 for an example of this issue.
67const char kSuspectedNetmaskPrefix[] = "255.";
68
Paul Stewartebd38562012-03-23 13:06:40 -070069} // namespace
70
Darin Petkov4e1b3f82012-09-27 13:22:37 +020071// TODO(petkov): Move to chromeos/dbus/service_constants.h.
72const char OpenVPNDriver::kOpenVPNCertProperty[] = "OpenVPN.Cert";
73const char OpenVPNDriver::kOpenVPNKeyProperty[] = "OpenVPN.Key";
Paul Stewartebd38562012-03-23 13:06:40 -070074// static
Darin Petkovc418b4b2012-10-05 11:42:52 +020075const char OpenVPNDriver::kDefaultCACertificates[] =
76 "/etc/ssl/certs/ca-certificates.crt";
Darin Petkovca8a0e62012-09-26 13:16:52 +020077// static
Paul Stewartebd38562012-03-23 13:06:40 -070078const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
79// static
Darin Petkov728faa92012-10-12 11:25:47 +020080const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
Paul Stewartebd38562012-03-23 13:06:40 -070081// static
Darin Petkovd4325392012-04-23 15:48:22 +020082const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020083 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
84 { flimflam::kOpenVPNAuthProperty, 0 },
85 { flimflam::kOpenVPNAuthRetryProperty, 0 },
86 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
87 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
88 { flimflam::kOpenVPNCaCertProperty, 0 },
89 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020090 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020091 { flimflam::kOpenVPNCompLZOProperty, 0 },
92 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
93 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
94 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020095 { flimflam::kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020096 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
97 { flimflam::kOpenVPNPasswordProperty,
98 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020099 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +0200100 { flimflam::kOpenVPNPortProperty, 0 },
101 { flimflam::kOpenVPNProtoProperty, 0 },
102 { flimflam::kOpenVPNProviderProperty, 0 },
103 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
104 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
105 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
106 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
107 { flimflam::kOpenVPNRenegSecProperty, 0 },
108 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
109 { flimflam::kOpenVPNShaperProperty, 0 },
110 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
111 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
112 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
113 { flimflam::kOpenVPNUserProperty, 0 },
114 { flimflam::kProviderHostProperty, 0 },
Darin Petkov1847d792012-04-17 11:33:06 +0200115 { flimflam::kProviderTypeProperty, 0 },
Paul Stewart5baebb72013-03-14 11:43:29 -0700116 { kOpenVPNCaCertPemProperty, 0 },
Darin Petkov1847d792012-04-17 11:33:06 +0200117 { kOpenVPNCertProperty, 0 },
118 { kOpenVPNKeyProperty, 0 },
119 { kOpenVPNPingExitProperty, 0 },
120 { kOpenVPNPingProperty, 0 },
121 { kOpenVPNPingRestartProperty, 0 },
122 { kOpenVPNTLSAuthProperty, 0 },
123 { kOpenVPNVerbProperty, 0 },
124 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700125
126 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200127 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100128};
Paul Stewart291a4732012-03-14 19:19:02 -0700129
Darin Petkov1a462de2012-05-02 11:10:48 +0200130const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
Darin Petkov1a462de2012-05-02 11:10:48 +0200131const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
Darin Petkov1a462de2012-05-02 11:10:48 +0200132const char OpenVPNDriver::kChromeOSReleaseVersion[] =
133 "CHROMEOS_RELEASE_VERSION";
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100134const int OpenVPNDriver::kReconnectOfflineTimeoutSeconds = 2 * 60;
135const int OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds = 20;
Darin Petkov1a462de2012-05-02 11:10:48 +0200136
Darin Petkova9b1fed2012-02-29 11:49:05 +0100137OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100138 EventDispatcher *dispatcher,
139 Metrics *metrics,
140 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800141 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700142 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +0200143 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200144 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100145 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800146 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100147 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200148 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200149 nss_(NSS::GetInstance()),
Paul Stewart5baebb72013-03-14 11:43:29 -0700150 certificate_file_(new CertificateFile(glib)),
Darin Petkov5a850472012-06-06 15:44:24 +0200151 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200152 lsb_release_file_(kLSBReleaseFile),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100153 pid_(0),
Darin Petkova5e07ef2012-07-09 14:27:57 +0200154 child_watch_tag_(0),
155 default_service_callback_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100156
Darin Petkov36a3ace2012-03-06 17:22:14 +0100157OpenVPNDriver::~OpenVPNDriver() {
Darin Petkovaba89322013-03-11 14:48:22 +0100158 IdleService();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100159}
160
Darin Petkovaba89322013-03-11 14:48:22 +0100161void OpenVPNDriver::IdleService() {
Darin Petkov1c049c72013-03-21 13:15:45 +0100162 Cleanup(Service::kStateIdle,
163 Service::kFailureUnknown,
164 Service::kErrorDetailsNone);
Darin Petkovaba89322013-03-11 14:48:22 +0100165}
166
Darin Petkov1c049c72013-03-21 13:15:45 +0100167void OpenVPNDriver::FailService(Service::ConnectFailure failure,
168 const string &error_details) {
169 Cleanup(Service::kStateFailure, failure, error_details);
Darin Petkovaba89322013-03-11 14:48:22 +0100170}
171
172void OpenVPNDriver::Cleanup(Service::ConnectState state,
Darin Petkov1c049c72013-03-21 13:15:45 +0100173 Service::ConnectFailure failure,
Darin Petkovaba89322013-03-11 14:48:22 +0100174 const string &error_details) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700175 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
Darin Petkovaba89322013-03-11 14:48:22 +0100176 << ", " << error_details << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200177 StopConnectTimeout();
Darin Petkov84a95532013-03-13 12:24:45 +0100178 if (child_watch_tag_) {
179 glib_->SourceRemove(child_watch_tag_);
180 child_watch_tag_ = 0;
181 }
182 // Disconnecting the management interface will terminate the openvpn
183 // process. Ensure this is handled robustly by first removing the child watch
184 // above and then terminating and reaping the process through ProcessKiller.
Darin Petkov46463022012-03-29 14:57:32 +0200185 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200186 if (!tls_auth_file_.empty()) {
187 file_util::Delete(tls_auth_file_, false);
188 tls_auth_file_.clear();
189 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200190 if (default_service_callback_tag_) {
191 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
192 default_service_callback_tag_ = 0;
193 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100194 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200195 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100196 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200197 interface_index = device_->interface_index();
Darin Petkov029d3532012-04-18 14:38:04 +0200198 device_->OnDisconnected();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500199 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100200 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200201 }
202 if (pid_) {
203 Closure callback;
204 if (interface_index >= 0) {
205 callback =
206 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
207 interface_index = -1;
208 }
209 process_killer_->Kill(pid_, callback);
210 pid_ = 0;
211 }
212 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100213 device_info_->DeleteInterface(interface_index);
214 }
215 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100216 if (service_) {
Darin Petkov1c049c72013-03-21 13:15:45 +0100217 if (state == Service::kStateFailure) {
218 service_->SetErrorDetails(error_details);
219 service_->SetFailure(failure);
220 } else {
221 service_->SetState(state);
222 }
Darin Petkov79d74c92012-03-07 17:20:32 +0100223 service_ = NULL;
224 }
Darin Petkov3189a472012-10-05 09:55:33 +0200225 ip_properties_ = IPConfig::Properties();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100226}
227
228bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700229 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100230
231 vector<string> options;
232 Error error;
233 InitOptions(&options, &error);
234 if (error.IsFailure()) {
235 return false;
236 }
Darin Petkov728faa92012-10-12 11:25:47 +0200237 LOG(INFO) << "OpenVPN process options: " << JoinString(options, ' ');
Darin Petkov36a3ace2012-03-06 17:22:14 +0100238
239 // TODO(petkov): This code needs to be abstracted away in a separate external
240 // process module (crosbug.com/27131).
241 vector<char *> process_args;
242 process_args.push_back(const_cast<char *>(kOpenVPNPath));
243 for (vector<string>::const_iterator it = options.begin();
244 it != options.end(); ++it) {
245 process_args.push_back(const_cast<char *>(it->c_str()));
246 }
247 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200248
249 vector<string> environment;
250 InitEnvironment(&environment);
251
252 vector<char *> process_env;
253 for (vector<string>::const_iterator it = environment.begin();
254 it != environment.end(); ++it) {
255 process_env.push_back(const_cast<char *>(it->c_str()));
256 }
257 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100258
259 CHECK(!pid_);
Darin Petkov68710d72013-02-13 14:22:56 +0100260 if (!glib_->SpawnAsync(NULL,
261 process_args.data(),
262 process_env.data(),
263 G_SPAWN_DO_NOT_REAP_CHILD,
264 NULL,
265 NULL,
266 &pid_,
267 NULL)) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100268 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
269 return false;
270 }
271 CHECK(!child_watch_tag_);
272 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
273 return true;
274}
275
276// static
277void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700278 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100279 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
280 me->child_watch_tag_ = 0;
281 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200282 me->pid_ = 0;
Darin Petkov1c049c72013-03-21 13:15:45 +0100283 me->FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100284 // TODO(petkov): Figure if we need to restart the connection.
285}
Darin Petkov33af05c2012-02-28 10:10:30 +0100286
Darin Petkov5a850472012-06-06 15:44:24 +0200287// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200288void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200289 int interface_index) {
290 if (device_info) {
291 LOG(INFO) << "Deleting interface " << interface_index;
292 device_info->DeleteInterface(interface_index);
293 }
294}
295
Paul Stewartca6abd42012-03-01 15:45:29 -0800296bool OpenVPNDriver::ClaimInterface(const string &link_name,
297 int interface_index) {
298 if (link_name != tunnel_interface_) {
299 return false;
300 }
301
Ben Chanfad4a0b2012-04-18 15:49:59 -0700302 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800303
Darin Petkovf20994f2012-03-05 16:12:19 +0100304 CHECK(!device_);
Darin Petkov602303f2012-06-06 12:15:59 +0200305 device_ = new VPN(control_, dispatcher(), metrics_, manager(),
Darin Petkovf20994f2012-03-05 16:12:19 +0100306 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500307 device_->SetEnabled(true);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500308
Darin Petkov36a3ace2012-03-06 17:22:14 +0100309 rpc_task_.reset(new RPCTask(control_, this));
Darin Petkov1c049c72013-03-21 13:15:45 +0100310 if (SpawnOpenVPN()) {
Darin Petkov68710d72013-02-13 14:22:56 +0100311 default_service_callback_tag_ =
312 manager()->RegisterDefaultServiceCallback(
313 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Darin Petkov1c049c72013-03-21 13:15:45 +0100314 } else {
315 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100316 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800317 return true;
318}
319
Darin Petkov209e6292012-04-20 11:33:32 +0200320void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
321 NOTREACHED();
322}
323
Darin Petkov36a3ace2012-03-06 17:22:14 +0100324void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100325 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200326 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100327 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100328 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100329 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100330 }
Darin Petkov3189a472012-10-05 09:55:33 +0200331 // On restart/reconnect, update the existing IP configuration.
332 ParseIPConfiguration(dict, &ip_properties_);
333 device_->SelectService(service_);
334 device_->UpdateIPConfig(ip_properties_);
Paul Stewart91a43cb2013-03-02 21:34:15 -0800335 ReportConnectionMetrics();
Darin Petkov602303f2012-06-06 12:15:59 +0200336 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100337}
338
339// static
340void OpenVPNDriver::ParseIPConfiguration(
341 const map<string, string> &configuration,
342 IPConfig::Properties *properties) {
343 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100344 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100345 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200346 if (!properties->subnet_prefix) {
347 properties->subnet_prefix =
348 IPAddress::GetMaxPrefixLength(properties->address_family);
349 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100350 for (map<string, string>::const_iterator it = configuration.begin();
351 it != configuration.end(); ++it) {
352 const string &key = it->first;
353 const string &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700354 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100355 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
356 properties->address = value;
357 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
358 properties->broadcast_address = value;
359 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700360 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100361 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
362 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
Paul Stewart4698c1a2013-05-16 15:42:19 -0700363 if (StartsWithASCII(value, kSuspectedNetmaskPrefix, false)) {
364 LOG(WARNING) << "Option " << key << " value " << value
365 << " looks more like a netmask than a peer address; "
366 << "assuming it is the former.";
367 // In this situation, the "peer_address" value will be left
368 // unset and Connection::UpdateFromIPConfig() will treat the
369 // interface as if it were a broadcast-style network. The
370 // kernel will, automatically set the peer address equal to
371 // the local address.
372 properties->subnet_prefix =
373 IPAddress::GetPrefixLengthFromMask(properties->address_family,
374 value);
375 } else {
376 properties->peer_address = value;
377 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100378 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
379 properties->gateway = value;
380 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700381 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100382 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
383 int mtu = 0;
384 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
385 properties->mtu = mtu;
386 } else {
387 LOG(ERROR) << "MTU " << value << " ignored.";
388 }
389 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
390 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
391 int order = 0;
392 if (base::StringToInt(suffix, &order)) {
393 foreign_options[order] = value;
394 } else {
395 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
396 }
397 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100398 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
399 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100400 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700401 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100402 }
403 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100404 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100405 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100406}
407
408// static
409void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
410 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200411 vector<string> domain_search;
412 vector<string> dns_servers;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100413 for (ForeignOptions::const_iterator it = options.begin();
414 it != options.end(); ++it) {
Darin Petkove8587e32012-07-02 13:56:07 +0200415 ParseForeignOption(it->second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100416 }
Darin Petkove8587e32012-07-02 13:56:07 +0200417 if (!domain_search.empty()) {
418 properties->domain_search.swap(domain_search);
419 }
420 LOG_IF(WARNING, properties->domain_search.empty())
421 << "No search domains provided.";
422 if (!dns_servers.empty()) {
423 properties->dns_servers.swap(dns_servers);
424 }
425 LOG_IF(WARNING, properties->dns_servers.empty())
426 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100427}
428
429// static
430void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200431 vector<string> *domain_search,
432 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700433 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100434 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200435 SplitString(option, ' ', &tokens);
436 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100437 return;
438 }
439 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200440 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100441 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200442 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100443 }
444}
445
Darin Petkov60596742012-03-05 12:17:17 +0100446// static
447IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
448 const string &prefix, const string &key, RouteOptions *routes) {
449 int order = 0;
450 if (!StartsWithASCII(key, prefix, false) ||
451 !base::StringToInt(key.substr(prefix.size()), &order)) {
452 return NULL;
453 }
454 return &(*routes)[order];
455}
456
457// static
458void OpenVPNDriver::ParseRouteOption(
459 const string &key, const string &value, RouteOptions *routes) {
460 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
461 if (route) {
462 route->host = value;
463 return;
464 }
465 route = GetRouteOptionEntry("netmask_", key, routes);
466 if (route) {
467 route->netmask = value;
468 return;
469 }
470 route = GetRouteOptionEntry("gateway_", key, routes);
471 if (route) {
472 route->gateway = value;
473 return;
474 }
475 LOG(WARNING) << "Unknown route option ignored: " << key;
476}
477
478// static
479void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
480 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200481 vector<IPConfig::Route> new_routes;
Darin Petkov60596742012-03-05 12:17:17 +0100482 for (RouteOptions::const_iterator it = routes.begin();
483 it != routes.end(); ++it) {
484 const IPConfig::Route &route = it->second;
485 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
486 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
487 continue;
488 }
Darin Petkove8587e32012-07-02 13:56:07 +0200489 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100490 }
Darin Petkove8587e32012-07-02 13:56:07 +0200491 if (!new_routes.empty()) {
492 properties->routes.swap(new_routes);
493 }
494 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100495}
496
Darin Petkov4b944842012-09-21 10:48:48 +0200497// static
498bool OpenVPNDriver::SplitPortFromHost(
499 const string &host, string *name, string *port) {
500 vector<string> tokens;
501 SplitString(host, ':', &tokens);
502 int port_number = 0;
503 if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
504 !IsAsciiDigit(tokens[1][0]) ||
505 !base::StringToInt(tokens[1], &port_number) || port_number > kuint16max) {
506 return false;
507 }
508 *name = tokens[0];
509 *port = tokens[1];
510 return true;
511}
512
Darin Petkov602303f2012-06-06 12:15:59 +0200513void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100514 StartConnectTimeout(kDefaultConnectTimeoutSeconds);
Darin Petkov79d74c92012-03-07 17:20:32 +0100515 service_ = service;
516 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100517 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
518 Error::PopulateAndLog(
519 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov1c049c72013-03-21 13:15:45 +0100520 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkovf20994f2012-03-05 16:12:19 +0100521 }
522 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100523}
524
Darin Petkovfe6a9372012-02-28 16:25:06 +0100525void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200526 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100527 if (vpnhost.empty()) {
528 Error::PopulateAndLog(
529 error, Error::kInvalidArguments, "VPN host not specified.");
530 return;
531 }
532 options->push_back("--client");
533 options->push_back("--tls-client");
Darin Petkov4b944842012-09-21 10:48:48 +0200534
Darin Petkovfe6a9372012-02-28 16:25:06 +0100535 options->push_back("--remote");
Darin Petkov4b944842012-09-21 10:48:48 +0200536 string host_name, host_port;
537 if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
538 DCHECK(!host_name.empty());
539 DCHECK(!host_port.empty());
540 options->push_back(host_name);
541 options->push_back(host_port);
542 } else {
543 options->push_back(vpnhost);
544 }
545
Darin Petkovfe6a9372012-02-28 16:25:06 +0100546 options->push_back("--nobind");
547 options->push_back("--persist-key");
548 options->push_back("--persist-tun");
549
Darin Petkovf20994f2012-03-05 16:12:19 +0100550 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800551 options->push_back("--dev");
552 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100553 options->push_back("--dev-type");
554 options->push_back("tun");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100555
Darin Petkov55771b72012-04-25 09:25:19 +0200556 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100557
Darin Petkovf3c71d72012-03-21 12:32:15 +0100558 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100559 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
560 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100561 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200562 {
563 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200564 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200565 if (!contents.empty()) {
566 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
567 file_util::WriteFile(
568 tls_auth_file_, contents.data(), contents.size()) !=
569 static_cast<int>(contents.size())) {
570 Error::PopulateAndLog(
571 error, Error::kInternalError, "Unable to setup tls-auth file.");
572 return;
573 }
574 options->push_back("--tls-auth");
575 options->push_back(tls_auth_file_.value());
576 }
577 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100578 AppendValueOption(
579 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
580 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
581 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
582 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
583 AppendValueOption(
584 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
585 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
586 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
587 AppendFlag(
588 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
589 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
590 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
591 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
592 "--server-poll-timeout", options);
593
Darin Petkovca8a0e62012-09-26 13:16:52 +0200594 if (!InitCAOptions(options, error)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200595 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200596 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100597
598 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100599 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
600 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
601 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100602
Darin Petkovfe6a9372012-02-28 16:25:06 +0100603 AppendValueOption(
604 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100605
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200606 InitClientAuthOptions(options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200607 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100608
609 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100610 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200611 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100612 if (remote_cert_tls.empty()) {
613 remote_cert_tls = "server";
614 }
615 if (remote_cert_tls != "none") {
616 options->push_back("--remote-cert-tls");
617 options->push_back(remote_cert_tls);
618 }
619
620 // This is an undocumented command line argument that works like a .cfg file
621 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
622 AppendValueOption(
623 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
624 // TODO(sleffler): Support more than one eku parameter.
625 AppendValueOption(
626 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
627 AppendValueOption(
628 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
629
Darin Petkove0d5dd12012-04-04 16:10:48 +0200630 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200631 return;
632 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100633
Darin Petkova9b1fed2012-02-29 11:49:05 +0100634 // Setup openvpn-script options and RPC information required to send back
635 // Layer 3 configuration.
636 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200637 options->push_back(kRPCTaskServiceVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100638 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
639 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200640 options->push_back(kRPCTaskPathVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100641 options->push_back(rpc_task_->GetRpcIdentifier());
642 options->push_back("--script-security");
643 options->push_back("2");
644 options->push_back("--up");
645 options->push_back(kOpenVPNScript);
646 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100647
648 // Disable openvpn handling since we do route+ifconfig work.
649 options->push_back("--route-noexec");
650 options->push_back("--ifconfig-noexec");
651
652 // Drop root privileges on connection and enable callback scripts to send
653 // notify messages.
654 options->push_back("--user");
655 options->push_back("openvpn");
656 options->push_back("--group");
657 options->push_back("openvpn");
658}
659
Darin Petkovca8a0e62012-09-26 13:16:52 +0200660bool OpenVPNDriver::InitCAOptions(vector<string> *options, Error *error) {
Darin Petkovc418b4b2012-10-05 11:42:52 +0200661 options->push_back("--ca");
Darin Petkovb451d6e2012-04-23 11:56:41 +0200662 string ca_cert =
Darin Petkovca8a0e62012-09-26 13:16:52 +0200663 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "");
664 string ca_cert_nss =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200665 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Paul Stewart5baebb72013-03-14 11:43:29 -0700666 string ca_cert_pem = args()->LookupString(kOpenVPNCaCertPemProperty, "");
667
668 int num_ca_cert_types = 0;
669 if (!ca_cert.empty())
670 num_ca_cert_types++;
671 if (!ca_cert_nss.empty())
672 num_ca_cert_types++;
673 if (!ca_cert_pem.empty())
674 num_ca_cert_types++;
675 if (num_ca_cert_types == 0) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200676 // Use default CAs if no CA certificate is provided.
Darin Petkovc418b4b2012-10-05 11:42:52 +0200677 options->push_back(kDefaultCACertificates);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200678 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700679 } else if (num_ca_cert_types > 1) {
680 Error::PopulateAndLog(
681 error, Error::kInvalidArguments,
682 "Can't specify more than one of CACert, CACertNSS and CACertPEM.");
Darin Petkovca8a0e62012-09-26 13:16:52 +0200683 return false;
684 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700685 string cert_file;
Darin Petkovca8a0e62012-09-26 13:16:52 +0200686 if (!ca_cert_nss.empty()) {
Paul Stewart5baebb72013-03-14 11:43:29 -0700687 DCHECK(ca_cert.empty() && ca_cert_pem.empty());
Darin Petkovb451d6e2012-04-23 11:56:41 +0200688 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200689 vector<char> id(vpnhost.begin(), vpnhost.end());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200690 FilePath certfile = nss_->GetPEMCertfile(ca_cert_nss, id);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200691 if (certfile.empty()) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200692 Error::PopulateAndLog(
693 error,
694 Error::kInvalidArguments,
695 "Unable to extract NSS CA certificate: " + ca_cert_nss);
696 return false;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200697 }
Darin Petkovca8a0e62012-09-26 13:16:52 +0200698 options->push_back(certfile.value());
699 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700700 } else if (!ca_cert_pem.empty()) {
701 DCHECK(ca_cert.empty() && ca_cert_nss.empty());
702 FilePath certfile = certificate_file_->CreatePEMFromString(ca_cert_pem);
703 if (certfile.empty()) {
704 Error::PopulateAndLog(
705 error,
706 Error::kInvalidArguments,
707 "Unable to extract PEM CA certificate.");
708 return false;
709 }
710 options->push_back(certfile.value());
711 return true;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200712 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700713 DCHECK(!ca_cert.empty() && ca_cert_nss.empty() && ca_cert_pem.empty());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200714 options->push_back(ca_cert);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200715 return true;
716}
717
718void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200719 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200720 if (!id.empty()) {
721 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200722 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200723 if (provider.empty()) {
724 provider = kDefaultPKCS11Provider;
725 }
726 options->push_back("--pkcs11-providers");
727 options->push_back(provider);
728 options->push_back("--pkcs11-id");
729 options->push_back(id);
730 }
731}
732
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200733void OpenVPNDriver::InitClientAuthOptions(vector<string> *options) {
734 bool has_cert = AppendValueOption(kOpenVPNCertProperty, "--cert", options);
735 bool has_key = AppendValueOption(kOpenVPNKeyProperty, "--key", options);
736 // If the AuthUserPass property is set, or the User property is non-empty, or
737 // there's neither a key, nor a cert available, specify user-password client
738 // authentication.
739 if (args()->ContainsString(flimflam::kOpenVPNAuthUserPassProperty) ||
740 !args()->LookupString(flimflam::kOpenVPNUserProperty, "").empty() ||
741 (!has_cert && !has_key)) {
742 options->push_back("--auth-user-pass");
743 }
744}
745
Darin Petkove0d5dd12012-04-04 16:10:48 +0200746bool OpenVPNDriver::InitManagementChannelOptions(
747 vector<string> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200748 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200749 Error::PopulateAndLog(
750 error, Error::kInternalError, "Unable to setup management channel.");
751 return false;
752 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200753 // If there's a connected default service already, allow the openvpn client to
754 // establish connection as soon as it's started. Otherwise, hold the client
755 // until an underlying service connects and OnDefaultServiceChanged is
756 // invoked.
Darin Petkov4cbff5b2013-01-29 16:29:05 +0100757 if (manager()->IsOnline()) {
Darin Petkova5e07ef2012-07-09 14:27:57 +0200758 management_server_->ReleaseHold();
759 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200760 return true;
761}
762
Darin Petkov55771b72012-04-25 09:25:19 +0200763void OpenVPNDriver::InitLoggingOptions(vector<string> *options) {
764 options->push_back("--syslog");
765
766 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
767 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
768 verb = "3";
769 }
770 if (!verb.empty()) {
771 options->push_back("--verb");
772 options->push_back(verb);
773 }
774}
775
Darin Petkov46463022012-03-29 14:57:32 +0200776bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100777 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200778 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100779 if (!value.empty()) {
780 options->push_back(option);
781 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200782 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100783 }
Darin Petkov46463022012-03-29 14:57:32 +0200784 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100785}
786
Darin Petkov46463022012-03-29 14:57:32 +0200787bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100788 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200789 if (args()->ContainsString(property)) {
Darin Petkovfe6a9372012-02-28 16:25:06 +0100790 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200791 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100792 }
Darin Petkov46463022012-03-29 14:57:32 +0200793 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100794}
795
Darin Petkov6aa21872012-03-09 16:10:19 +0100796void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700797 SLOG(VPN, 2) << __func__;
Darin Petkovaba89322013-03-11 14:48:22 +0100798 IdleService();
Darin Petkov6aa21872012-03-09 16:10:19 +0100799}
800
Darin Petkov5eb05422012-05-11 15:45:25 +0200801void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkova42afe32013-02-05 16:53:52 +0100802 LOG(INFO) << "Underlying connection disconnected.";
803 // Restart the OpenVPN client forcing a reconnect attempt.
804 management_server_->Restart();
805 // Indicate reconnect state right away to drop the VPN connection and start
806 // the connect timeout. This ensures that any miscommunication between shill
807 // and openvpn will not lead to a permanently stale connectivity state. Note
808 // that a subsequent invocation of OnReconnecting due to a RECONNECTING
809 // message will essentially be a no-op.
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100810 OnReconnecting(kReconnectReasonOffline);
Darin Petkova42afe32013-02-05 16:53:52 +0100811}
812
813void OpenVPNDriver::OnConnectTimeout() {
814 VPNDriver::OnConnectTimeout();
Darin Petkov1c049c72013-03-21 13:15:45 +0100815 Service::ConnectFailure failure =
816 management_server_->state() == OpenVPNManagementServer::kStateResolve ?
817 Service::kFailureDNSLookup : Service::kFailureConnect;
818 FailService(failure, Service::kErrorDetailsNone);
Darin Petkov5eb05422012-05-11 15:45:25 +0200819}
820
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100821void OpenVPNDriver::OnReconnecting(ReconnectReason reason) {
822 LOG(INFO) << __func__ << "(" << reason << ")";
823 int timeout_seconds = GetReconnectTimeoutSeconds(reason);
824 if (reason == kReconnectReasonTLSError &&
825 timeout_seconds < connect_timeout_seconds()) {
826 // Reconnect due to TLS error happens during connect so we need to cancel
827 // the original connect timeout first and then reduce the time limit.
828 StopConnectTimeout();
829 }
830 StartConnectTimeout(timeout_seconds);
Darin Petkova5e07ef2012-07-09 14:27:57 +0200831 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
832 // might be in hold state if the VPN connection was previously established
833 // successfully. The hold will be released by OnDefaultServiceChanged when a
834 // new default service connects. This ensures that the client will use a fully
835 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200836 if (device_) {
837 device_->OnDisconnected();
838 }
839 if (service_) {
840 service_->SetState(Service::kStateAssociating);
841 }
842}
843
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100844// static
845int OpenVPNDriver::GetReconnectTimeoutSeconds(ReconnectReason reason) {
846 switch (reason) {
847 case kReconnectReasonOffline:
848 return kReconnectOfflineTimeoutSeconds;
849 case kReconnectReasonTLSError:
850 return kReconnectTLSErrorTimeoutSeconds;
851 default:
852 break;
853 }
854 return kDefaultConnectTimeoutSeconds;
855}
856
Paul Stewart39964fa2012-04-04 09:50:25 -0700857string OpenVPNDriver::GetProviderType() const {
858 return flimflam::kProviderOpenVpn;
859}
860
Darin Petkovb536a742012-04-26 11:31:28 +0200861KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
862 SLOG(VPN, 2) << __func__;
863 KeyValueStore props = VPNDriver::GetProvider(error);
864 props.SetBool(flimflam::kPassphraseRequiredProperty,
865 args()->LookupString(
866 flimflam::kOpenVPNPasswordProperty, "").empty());
867 return props;
868}
869
Darin Petkov1a462de2012-05-02 11:10:48 +0200870// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
871// singleton if it's used outside OpenVPN.
872bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
873 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
874 string contents;
875 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
876 LOG(ERROR) << "Unable to read the lsb-release file: "
877 << lsb_release_file_.value();
878 return false;
879 }
880 vector<string> lines;
881 SplitString(contents, '\n', &lines);
882 for (vector<string>::const_iterator it = lines.begin();
883 it != lines.end(); ++it) {
884 size_t assign = it->find('=');
885 if (assign == string::npos) {
886 continue;
887 }
888 (*lsb_release)[it->substr(0, assign)] = it->substr(assign + 1);
889 }
890 return true;
891}
892
893void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
894 // Adds the platform name and version to the environment so that openvpn can
895 // send them to the server when OpenVPN.PushPeerInfo is set.
896 map<string, string> lsb_release;
897 ParseLSBRelease(&lsb_release);
898 string platform_name = lsb_release[kChromeOSReleaseName];
899 if (!platform_name.empty()) {
900 environment->push_back("IV_PLAT=" + platform_name);
901 }
902 string platform_version = lsb_release[kChromeOSReleaseVersion];
903 if (!platform_version.empty()) {
904 environment->push_back("IV_PLAT_REL=" + platform_version);
905 }
906}
907
Darin Petkova5e07ef2012-07-09 14:27:57 +0200908void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
909 SLOG(VPN, 2) << __func__
Darin Petkov457728b2013-01-09 09:49:08 +0100910 << "(" << (service ? service->unique_name() : "-") << ")";
Darin Petkova5e07ef2012-07-09 14:27:57 +0200911 // Allow the openvpn client to connect/reconnect only over a connected
912 // underlying default service. If there's no default connected service, hold
913 // the openvpn client until an underlying connection is established. If the
914 // default service is our VPN service, hold the openvpn client on reconnect so
915 // that the VPN connection can be torn down fully before a new connection
916 // attempt is made over the underlying service.
917 if (service && service != service_ && service->IsConnected()) {
918 management_server_->ReleaseHold();
919 } else {
920 management_server_->Hold();
921 }
922}
923
Paul Stewart91a43cb2013-03-02 21:34:15 -0800924void OpenVPNDriver::ReportConnectionMetrics() {
925 metrics_->SendEnumToUMA(
926 Metrics::kMetricVpnDriver,
927 Metrics::kVpnDriverOpenVpn,
928 Metrics::kMetricVpnDriverMax);
929
Paul Stewarte8e71da2013-03-20 08:48:33 -0700930 if (args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "") != "" ||
931 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800932 metrics_->SendEnumToUMA(
933 Metrics::kMetricVpnRemoteAuthenticationType,
934 Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
935 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
936 } else {
937 metrics_->SendEnumToUMA(
938 Metrics::kMetricVpnRemoteAuthenticationType,
939 Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
940 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
941 }
942
943 bool has_user_authentication = false;
Paul Stewarte8e71da2013-03-20 08:48:33 -0700944 if (args()->LookupString(flimflam::kOpenVPNOTPProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800945 metrics_->SendEnumToUMA(
946 Metrics::kMetricVpnUserAuthenticationType,
947 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
948 Metrics::kMetricVpnUserAuthenticationTypeMax);
949 has_user_authentication = true;
950 }
Paul Stewarte8e71da2013-03-20 08:48:33 -0700951 if (args()->LookupString(flimflam::kOpenVPNAuthUserPassProperty, "") != ""||
952 args()->LookupString(flimflam::kOpenVPNUserProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800953 metrics_->SendEnumToUMA(
954 Metrics::kMetricVpnUserAuthenticationType,
955 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
956 Metrics::kMetricVpnUserAuthenticationTypeMax);
957 has_user_authentication = true;
958 }
Paul Stewarte8e71da2013-03-20 08:48:33 -0700959 if (args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "") != "" ||
960 args()->LookupString(kOpenVPNCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800961 metrics_->SendEnumToUMA(
962 Metrics::kMetricVpnUserAuthenticationType,
963 Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
964 Metrics::kMetricVpnUserAuthenticationTypeMax);
965 has_user_authentication = true;
966 }
967 if (!has_user_authentication) {
968 metrics_->SendEnumToUMA(
969 Metrics::kMetricVpnUserAuthenticationType,
970 Metrics::kVpnUserAuthenticationTypeOpenVpnNone,
971 Metrics::kMetricVpnUserAuthenticationTypeMax);
972 }
973}
974
Darin Petkov33af05c2012-02-28 10:10:30 +0100975} // namespace shill