blob: 998497cb545ba3bf5400d78d41d088dba34b2282 [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>
Ben Chana0ddf462014-02-06 11:32:42 -080010#include <base/strings/string_number_conversions.h>
11#include <base/strings/string_split.h>
12#include <base/strings/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 Petkov46463022012-03-29 14:57:32 +020022#include "shill/openvpn_management_server.h"
Darin Petkov5a850472012-06-06 15:44:24 +020023#include "shill/process_killer.h"
Darin Petkova9b1fed2012-02-29 11:49:05 +010024#include "shill/rpc_task.h"
Darin Petkov46463022012-03-29 14:57:32 +020025#include "shill/sockets.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070026#include "shill/virtual_device.h"
Darin Petkov79d74c92012-03-07 17:20:32 +010027#include "shill/vpn_service.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010028
Darin Petkov5a850472012-06-06 15:44:24 +020029using base::Closure;
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080030using base::FilePath;
Darin Petkov78f63262012-03-26 01:30:24 +020031using base::SplitString;
Darin Petkova5e07ef2012-07-09 14:27:57 +020032using base::Unretained;
Darin Petkov5a850472012-06-06 15:44:24 +020033using base::WeakPtr;
Darin Petkov14c29ec2012-03-02 11:34:19 +010034using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010035using std::string;
36using std::vector;
37
Darin Petkov33af05c2012-02-28 10:10:30 +010038namespace shill {
39
Darin Petkova9b1fed2012-02-29 11:49:05 +010040namespace {
Darin Petkov5a850472012-06-06 15:44:24 +020041
Darin Petkov14c29ec2012-03-02 11:34:19 +010042const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
43const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
44const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
45const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
46const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
Paul Stewarta64b7d72014-05-09 10:11:39 -070047const char kOpenVPNRedirectGateway[] = "redirect_gateway";
48const char kOpenVPNRedirectPrivate[] = "redirect_private";
Darin Petkov14c29ec2012-03-02 11:34:19 +010049const char kOpenVPNRouteOptionPrefix[] = "route_";
50const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
51const char kOpenVPNTrustedIP[] = "trusted_ip";
52const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010053
Darin Petkove0d5dd12012-04-04 16:10:48 +020054const char kDefaultPKCS11Provider[] = "libchaps.so";
55
Paul Stewart4698c1a2013-05-16 15:42:19 -070056// Some configurations pass the netmask in the ifconfig_remote property.
57// This is due to some servers not explicitly indicating that they are using
58// a "broadcast mode" network instead of peer-to-peer. See
59// http://crbug.com/241264 for an example of this issue.
60const char kSuspectedNetmaskPrefix[] = "255.";
61
Paul Stewartebd38562012-03-23 13:06:40 -070062} // namespace
63
64// static
Darin Petkovc418b4b2012-10-05 11:42:52 +020065const char OpenVPNDriver::kDefaultCACertificates[] =
66 "/etc/ssl/certs/ca-certificates.crt";
Darin Petkovca8a0e62012-09-26 13:16:52 +020067// static
Paul Stewartebd38562012-03-23 13:06:40 -070068const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
69// static
Darin Petkov728faa92012-10-12 11:25:47 +020070const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
Paul Stewartebd38562012-03-23 13:06:40 -070071// static
Darin Petkovd4325392012-04-23 15:48:22 +020072const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Ben Chan73728782013-09-20 13:40:54 -070073 { kOpenVPNAuthNoCacheProperty, 0 },
74 { kOpenVPNAuthProperty, 0 },
75 { kOpenVPNAuthRetryProperty, 0 },
76 { kOpenVPNAuthUserPassProperty, 0 },
77 { kOpenVPNCaCertNSSProperty, 0 },
78 { kOpenVPNCaCertProperty, 0 },
79 { kOpenVPNCipherProperty, 0 },
80 { kOpenVPNClientCertIdProperty, Property::kCredential },
81 { kOpenVPNCompLZOProperty, 0 },
82 { kOpenVPNCompNoAdaptProperty, 0 },
Paul Stewartc8e3ef92014-05-08 13:27:25 -070083 { kOpenVPNIgnoreDefaultRouteProperty, 0 },
Ben Chan73728782013-09-20 13:40:54 -070084 { kOpenVPNKeyDirectionProperty, 0 },
85 { kOpenVPNNsCertTypeProperty, 0 },
86 { kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020087 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
Ben Chan73728782013-09-20 13:40:54 -070088 { kOpenVPNPasswordProperty, Property::kCredential | Property::kWriteOnly },
89 { kOpenVPNPinProperty, Property::kCredential },
90 { kOpenVPNPortProperty, 0 },
91 { kOpenVPNProtoProperty, 0 },
92 { kOpenVPNProviderProperty, 0 },
93 { kOpenVPNPushPeerInfoProperty, 0 },
94 { kOpenVPNRemoteCertEKUProperty, 0 },
95 { kOpenVPNRemoteCertKUProperty, 0 },
96 { kOpenVPNRemoteCertTLSProperty, 0 },
97 { kOpenVPNRenegSecProperty, 0 },
98 { kOpenVPNServerPollTimeoutProperty, 0 },
99 { kOpenVPNShaperProperty, 0 },
100 { kOpenVPNStaticChallengeProperty, 0 },
101 { kOpenVPNTLSAuthContentsProperty, 0 },
102 { kOpenVPNTLSRemoteProperty, 0 },
Paul Stewartb5768232014-02-27 07:21:34 -0800103 { kOpenVPNTokenProperty,
104 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
Ben Chan73728782013-09-20 13:40:54 -0700105 { kOpenVPNUserProperty, 0 },
106 { kProviderHostProperty, 0 },
107 { kProviderTypeProperty, 0 },
Paul Stewart0f9c9302013-06-14 15:41:28 -0700108 { kOpenVPNCaCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200109 { kOpenVPNCertProperty, 0 },
Paul Stewartf3b6d572013-06-17 09:35:33 -0700110 { kOpenVPNExtraCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200111 { kOpenVPNKeyProperty, 0 },
112 { kOpenVPNPingExitProperty, 0 },
113 { kOpenVPNPingProperty, 0 },
114 { kOpenVPNPingRestartProperty, 0 },
115 { kOpenVPNTLSAuthProperty, 0 },
116 { kOpenVPNVerbProperty, 0 },
Paul Stewart8343c0b2013-09-30 11:58:54 -0700117 { kOpenVPNVerifyHashProperty, 0 },
118 { kOpenVPNVerifyX509NameProperty, 0 },
119 { kOpenVPNVerifyX509TypeProperty, 0 },
Darin Petkov1847d792012-04-17 11:33:06 +0200120 { kVPNMTUProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100121};
Paul Stewart291a4732012-03-14 19:19:02 -0700122
Darin Petkov1a462de2012-05-02 11:10:48 +0200123const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
Darin Petkov1a462de2012-05-02 11:10:48 +0200124const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
Darin Petkov1a462de2012-05-02 11:10:48 +0200125const char OpenVPNDriver::kChromeOSReleaseVersion[] =
126 "CHROMEOS_RELEASE_VERSION";
Paul Stewartb26347a2013-08-02 12:12:09 -0700127
128// Directory where OpenVPN configuration files are exported while the
129// process is running.
130const char OpenVPNDriver::kDefaultOpenVPNConfigurationDirectory[] =
131 RUNDIR "/openvpn_config";
132
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100133const int OpenVPNDriver::kReconnectOfflineTimeoutSeconds = 2 * 60;
134const int OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds = 20;
Darin Petkov1a462de2012-05-02 11:10:48 +0200135
Darin Petkova9b1fed2012-02-29 11:49:05 +0100136OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100137 EventDispatcher *dispatcher,
138 Metrics *metrics,
139 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800140 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700141 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +0200142 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200143 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100144 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800145 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100146 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200147 management_server_(new OpenVPNManagementServer(this, glib)),
Paul Stewarteb713e82013-06-28 14:51:54 -0700148 certificate_file_(new CertificateFile()),
Paul Stewart8343c0b2013-09-30 11:58:54 -0700149 extra_certificates_file_(new CertificateFile()),
Darin Petkov5a850472012-06-06 15:44:24 +0200150 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200151 lsb_release_file_(kLSBReleaseFile),
Paul Stewartb26347a2013-08-02 12:12:09 -0700152 openvpn_config_directory_(kDefaultOpenVPNConfigurationDirectory),
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()) {
Ben Chana0ddf462014-02-06 11:32:42 -0800187 base::DeleteFile(tls_auth_file_, false);
Darin Petkov1fa81942012-04-02 11:38:08 +0200188 tls_auth_file_.clear();
189 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700190 if (!openvpn_config_file_.empty()) {
Ben Chana0ddf462014-02-06 11:32:42 -0800191 base::DeleteFile(openvpn_config_file_, false);
Paul Stewartb26347a2013-08-02 12:12:09 -0700192 openvpn_config_file_.clear();
193 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200194 if (default_service_callback_tag_) {
195 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
196 default_service_callback_tag_ = 0;
197 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100198 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200199 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100200 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200201 interface_index = device_->interface_index();
mukesh agrawal9da07772013-05-15 14:15:17 -0700202 device_->DropConnection();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500203 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100204 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200205 }
206 if (pid_) {
207 Closure callback;
208 if (interface_index >= 0) {
209 callback =
210 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
211 interface_index = -1;
212 }
213 process_killer_->Kill(pid_, callback);
214 pid_ = 0;
215 }
216 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100217 device_info_->DeleteInterface(interface_index);
218 }
219 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100220 if (service_) {
Darin Petkov1c049c72013-03-21 13:15:45 +0100221 if (state == Service::kStateFailure) {
222 service_->SetErrorDetails(error_details);
223 service_->SetFailure(failure);
224 } else {
225 service_->SetState(state);
226 }
Darin Petkov79d74c92012-03-07 17:20:32 +0100227 service_ = NULL;
228 }
Darin Petkov3189a472012-10-05 09:55:33 +0200229 ip_properties_ = IPConfig::Properties();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100230}
231
Paul Stewart406c4732013-08-01 09:30:12 -0700232// static
Paul Stewartb26347a2013-08-02 12:12:09 -0700233string OpenVPNDriver::JoinOptions(const vector<vector<string>> &options,
234 char separator) {
Paul Stewart406c4732013-08-01 09:30:12 -0700235 vector<string> option_strings;
236 for (const auto &option : options) {
Paul Stewart7590d6f2013-08-07 12:47:39 -0700237 vector<string> quoted_option;
238 for (const auto &argument : option) {
239 if (argument.find(' ') != string::npos ||
240 argument.find('\t') != string::npos ||
241 argument.find('"') != string::npos ||
242 argument.find(separator) != string::npos) {
243 string quoted_argument(argument);
244 const char separator_chars[] = { separator, '\0' };
Ben Chana0ddf462014-02-06 11:32:42 -0800245 base::ReplaceChars(argument, separator_chars, " ", &quoted_argument);
246 base::ReplaceChars(quoted_argument, "\\", "\\\\", &quoted_argument);
247 base::ReplaceChars(quoted_argument, "\"", "\\\"", &quoted_argument);
Paul Stewart7590d6f2013-08-07 12:47:39 -0700248 quoted_option.push_back("\"" + quoted_argument + "\"");
249 } else {
250 quoted_option.push_back(argument);
251 }
252 }
253 option_strings.push_back(JoinString(quoted_option, ' '));
Paul Stewart406c4732013-08-01 09:30:12 -0700254 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700255 return JoinString(option_strings, separator);
256}
257
258bool OpenVPNDriver::WriteConfigFile(
259 const vector<vector<string>> &options,
260 FilePath *config_file) {
Ben Chana0ddf462014-02-06 11:32:42 -0800261 if (!base::DirectoryExists(openvpn_config_directory_)) {
262 if (!base::CreateDirectory(openvpn_config_directory_)) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700263 LOG(ERROR) << "Unable to create configuration directory "
264 << openvpn_config_directory_.value();
265 return false;
266 }
267 if (chmod(openvpn_config_directory_.value().c_str(), S_IRWXU)) {
268 LOG(ERROR) << "Failed to set permissions on "
269 << openvpn_config_directory_.value();
Ben Chana0ddf462014-02-06 11:32:42 -0800270 base::DeleteFile(openvpn_config_directory_, true);
Paul Stewartb26347a2013-08-02 12:12:09 -0700271 return false;
272 }
273 }
274
275 string contents = JoinOptions(options, '\n');
276 contents.push_back('\n');
Ben Chana0ddf462014-02-06 11:32:42 -0800277 if (!base::CreateTemporaryFileInDir(openvpn_config_directory_, config_file) ||
Ben Chan6fbf64f2014-05-21 18:07:01 -0700278 base::WriteFile(*config_file, contents.data(), contents.size()) !=
Ben Chana0ddf462014-02-06 11:32:42 -0800279 static_cast<int>(contents.size())) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700280 LOG(ERROR) << "Unable to setup OpenVPN config file.";
281 return false;
282 }
283 return true;
Paul Stewart406c4732013-08-01 09:30:12 -0700284}
285
Darin Petkov36a3ace2012-03-06 17:22:14 +0100286bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700287 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100288
Paul Stewart406c4732013-08-01 09:30:12 -0700289 vector<vector<string>> options;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100290 Error error;
291 InitOptions(&options, &error);
292 if (error.IsFailure()) {
293 return false;
294 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700295 LOG(INFO) << "OpenVPN process options: " << JoinOptions(options, ',');
296 if (!WriteConfigFile(options, &openvpn_config_file_)) {
297 return false;
298 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100299
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700300 // TODO(quiche): This should be migrated to use ExternalTask.
301 // (crbug.com/246263).
Darin Petkov36a3ace2012-03-06 17:22:14 +0100302 vector<char *> process_args;
303 process_args.push_back(const_cast<char *>(kOpenVPNPath));
Paul Stewartb26347a2013-08-02 12:12:09 -0700304 process_args.push_back(const_cast<char *>("--config"));
305 process_args.push_back(const_cast<char *>(
306 openvpn_config_file_.value().c_str()));
Darin Petkov36a3ace2012-03-06 17:22:14 +0100307 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200308
309 vector<string> environment;
310 InitEnvironment(&environment);
311
312 vector<char *> process_env;
Paul Stewart406c4732013-08-01 09:30:12 -0700313 for (const auto &environment_variable : environment) {
314 process_env.push_back(const_cast<char *>(environment_variable.c_str()));
Darin Petkov1a462de2012-05-02 11:10:48 +0200315 }
316 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100317
318 CHECK(!pid_);
Darin Petkov68710d72013-02-13 14:22:56 +0100319 if (!glib_->SpawnAsync(NULL,
320 process_args.data(),
321 process_env.data(),
322 G_SPAWN_DO_NOT_REAP_CHILD,
323 NULL,
324 NULL,
325 &pid_,
326 NULL)) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100327 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
328 return false;
329 }
330 CHECK(!child_watch_tag_);
331 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
332 return true;
333}
334
335// static
336void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700337 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100338 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
339 me->child_watch_tag_ = 0;
340 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200341 me->pid_ = 0;
Darin Petkov1c049c72013-03-21 13:15:45 +0100342 me->FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100343 // TODO(petkov): Figure if we need to restart the connection.
344}
Darin Petkov33af05c2012-02-28 10:10:30 +0100345
Darin Petkov5a850472012-06-06 15:44:24 +0200346// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200347void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200348 int interface_index) {
349 if (device_info) {
350 LOG(INFO) << "Deleting interface " << interface_index;
351 device_info->DeleteInterface(interface_index);
352 }
353}
354
Paul Stewartca6abd42012-03-01 15:45:29 -0800355bool OpenVPNDriver::ClaimInterface(const string &link_name,
356 int interface_index) {
357 if (link_name != tunnel_interface_) {
358 return false;
359 }
360
Ben Chanfad4a0b2012-04-18 15:49:59 -0700361 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800362
Darin Petkovf20994f2012-03-05 16:12:19 +0100363 CHECK(!device_);
mukesh agrawal9da07772013-05-15 14:15:17 -0700364 device_ = new VirtualDevice(control_, dispatcher(), metrics_, manager(),
365 link_name, interface_index, Technology::kVPN);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500366 device_->SetEnabled(true);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500367
Darin Petkov36a3ace2012-03-06 17:22:14 +0100368 rpc_task_.reset(new RPCTask(control_, this));
Darin Petkov1c049c72013-03-21 13:15:45 +0100369 if (SpawnOpenVPN()) {
Darin Petkov68710d72013-02-13 14:22:56 +0100370 default_service_callback_tag_ =
371 manager()->RegisterDefaultServiceCallback(
372 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Darin Petkov1c049c72013-03-21 13:15:45 +0100373 } else {
374 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100375 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800376 return true;
377}
378
Darin Petkov209e6292012-04-20 11:33:32 +0200379void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
380 NOTREACHED();
381}
382
Darin Petkov36a3ace2012-03-06 17:22:14 +0100383void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100384 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200385 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100386 if (reason != "up") {
mukesh agrawal9da07772013-05-15 14:15:17 -0700387 device_->DropConnection();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100388 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100389 }
Darin Petkov3189a472012-10-05 09:55:33 +0200390 // On restart/reconnect, update the existing IP configuration.
391 ParseIPConfiguration(dict, &ip_properties_);
392 device_->SelectService(service_);
393 device_->UpdateIPConfig(ip_properties_);
Paul Stewart91a43cb2013-03-02 21:34:15 -0800394 ReportConnectionMetrics();
Darin Petkov602303f2012-06-06 12:15:59 +0200395 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100396}
397
Darin Petkov14c29ec2012-03-02 11:34:19 +0100398void OpenVPNDriver::ParseIPConfiguration(
399 const map<string, string> &configuration,
Paul Stewartc8e3ef92014-05-08 13:27:25 -0700400 IPConfig::Properties *properties) const {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100401 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100402 RouteOptions routes;
Paul Stewarta64b7d72014-05-09 10:11:39 -0700403 bool is_gateway_route_required = false;
404
Darin Petkov14c29ec2012-03-02 11:34:19 +0100405 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200406 if (!properties->subnet_prefix) {
407 properties->subnet_prefix =
408 IPAddress::GetMaxPrefixLength(properties->address_family);
409 }
Paul Stewart406c4732013-08-01 09:30:12 -0700410 for (const auto &configuration_map : configuration) {
411 const string &key = configuration_map.first;
412 const string &value = configuration_map.second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700413 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100414 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
415 properties->address = value;
416 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
417 properties->broadcast_address = value;
418 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700419 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100420 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
421 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
Paul Stewart4698c1a2013-05-16 15:42:19 -0700422 if (StartsWithASCII(value, kSuspectedNetmaskPrefix, false)) {
423 LOG(WARNING) << "Option " << key << " value " << value
424 << " looks more like a netmask than a peer address; "
425 << "assuming it is the former.";
426 // In this situation, the "peer_address" value will be left
427 // unset and Connection::UpdateFromIPConfig() will treat the
428 // interface as if it were a broadcast-style network. The
429 // kernel will, automatically set the peer address equal to
430 // the local address.
431 properties->subnet_prefix =
432 IPAddress::GetPrefixLengthFromMask(properties->address_family,
433 value);
434 } else {
435 properties->peer_address = value;
436 }
Paul Stewarta64b7d72014-05-09 10:11:39 -0700437 } else if (LowerCaseEqualsASCII(key, kOpenVPNRedirectGateway) ||
438 LowerCaseEqualsASCII(key, kOpenVPNRedirectPrivate)) {
439 is_gateway_route_required = true;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100440 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
Paul Stewarta64b7d72014-05-09 10:11:39 -0700441 properties->gateway = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100442 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700443 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100444 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
445 int mtu = 0;
446 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
447 properties->mtu = mtu;
448 } else {
449 LOG(ERROR) << "MTU " << value << " ignored.";
450 }
451 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
452 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
453 int order = 0;
454 if (base::StringToInt(suffix, &order)) {
455 foreign_options[order] = value;
456 } else {
457 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
458 }
459 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100460 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
461 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100462 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700463 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100464 }
465 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100466 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100467 SetRoutes(routes, properties);
Paul Stewarta64b7d72014-05-09 10:11:39 -0700468
469 if (const_args()->ContainsString(kOpenVPNIgnoreDefaultRouteProperty)) {
470 if (is_gateway_route_required) {
471 LOG(INFO) << "Configuration request to ignore default route is "
472 << "overridden by the remote server.";
473 } else {
474 SLOG(VPN, 2) << "Ignoring default route parameter as requested by "
475 << "configuration.";
476 properties->gateway.clear();
477 }
478 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100479}
480
481// static
482void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
483 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200484 vector<string> domain_search;
485 vector<string> dns_servers;
Paul Stewart406c4732013-08-01 09:30:12 -0700486 for (const auto &option_map : options) {
487 ParseForeignOption(option_map.second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100488 }
Darin Petkove8587e32012-07-02 13:56:07 +0200489 if (!domain_search.empty()) {
490 properties->domain_search.swap(domain_search);
491 }
492 LOG_IF(WARNING, properties->domain_search.empty())
493 << "No search domains provided.";
494 if (!dns_servers.empty()) {
495 properties->dns_servers.swap(dns_servers);
496 }
497 LOG_IF(WARNING, properties->dns_servers.empty())
498 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100499}
500
501// static
502void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200503 vector<string> *domain_search,
504 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700505 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100506 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200507 SplitString(option, ' ', &tokens);
508 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100509 return;
510 }
511 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200512 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100513 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200514 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100515 }
516}
517
Darin Petkov60596742012-03-05 12:17:17 +0100518// static
519IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
520 const string &prefix, const string &key, RouteOptions *routes) {
521 int order = 0;
522 if (!StartsWithASCII(key, prefix, false) ||
523 !base::StringToInt(key.substr(prefix.size()), &order)) {
524 return NULL;
525 }
526 return &(*routes)[order];
527}
528
529// static
530void OpenVPNDriver::ParseRouteOption(
531 const string &key, const string &value, RouteOptions *routes) {
532 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
533 if (route) {
534 route->host = value;
535 return;
536 }
537 route = GetRouteOptionEntry("netmask_", key, routes);
538 if (route) {
539 route->netmask = value;
540 return;
541 }
542 route = GetRouteOptionEntry("gateway_", key, routes);
543 if (route) {
544 route->gateway = value;
545 return;
546 }
547 LOG(WARNING) << "Unknown route option ignored: " << key;
548}
549
550// static
551void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
552 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200553 vector<IPConfig::Route> new_routes;
Paul Stewart406c4732013-08-01 09:30:12 -0700554 for (const auto &route_map : routes) {
555 const IPConfig::Route &route = route_map.second;
Darin Petkov60596742012-03-05 12:17:17 +0100556 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
Paul Stewart406c4732013-08-01 09:30:12 -0700557 LOG(WARNING) << "Ignoring incomplete route: " << route_map.first;
Darin Petkov60596742012-03-05 12:17:17 +0100558 continue;
559 }
Darin Petkove8587e32012-07-02 13:56:07 +0200560 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100561 }
Darin Petkove8587e32012-07-02 13:56:07 +0200562 if (!new_routes.empty()) {
563 properties->routes.swap(new_routes);
564 }
565 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100566}
567
Darin Petkov4b944842012-09-21 10:48:48 +0200568// static
569bool OpenVPNDriver::SplitPortFromHost(
570 const string &host, string *name, string *port) {
571 vector<string> tokens;
572 SplitString(host, ':', &tokens);
573 int port_number = 0;
574 if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
575 !IsAsciiDigit(tokens[1][0]) ||
576 !base::StringToInt(tokens[1], &port_number) || port_number > kuint16max) {
577 return false;
578 }
579 *name = tokens[0];
580 *port = tokens[1];
581 return true;
582}
583
Darin Petkov602303f2012-06-06 12:15:59 +0200584void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100585 StartConnectTimeout(kDefaultConnectTimeoutSeconds);
Darin Petkov79d74c92012-03-07 17:20:32 +0100586 service_ = service;
587 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100588 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
589 Error::PopulateAndLog(
590 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov1c049c72013-03-21 13:15:45 +0100591 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkovf20994f2012-03-05 16:12:19 +0100592 }
593 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100594}
595
Paul Stewart406c4732013-08-01 09:30:12 -0700596void OpenVPNDriver::InitOptions(vector<vector<string>> *options, Error *error) {
Ben Chan73728782013-09-20 13:40:54 -0700597 string vpnhost = args()->LookupString(kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100598 if (vpnhost.empty()) {
599 Error::PopulateAndLog(
600 error, Error::kInvalidArguments, "VPN host not specified.");
601 return;
602 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700603 AppendOption("client", options);
604 AppendOption("tls-client", options);
Darin Petkov4b944842012-09-21 10:48:48 +0200605
Darin Petkov4b944842012-09-21 10:48:48 +0200606 string host_name, host_port;
607 if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
608 DCHECK(!host_name.empty());
609 DCHECK(!host_port.empty());
Paul Stewartb26347a2013-08-02 12:12:09 -0700610 AppendOption("remote", host_name, host_port, options);
Darin Petkov4b944842012-09-21 10:48:48 +0200611 } else {
Paul Stewartb26347a2013-08-02 12:12:09 -0700612 AppendOption("remote", vpnhost, options);
Darin Petkov4b944842012-09-21 10:48:48 +0200613 }
614
Paul Stewartb26347a2013-08-02 12:12:09 -0700615 AppendOption("nobind", options);
616 AppendOption("persist-key", options);
617 AppendOption("persist-tun", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100618
Darin Petkovf20994f2012-03-05 16:12:19 +0100619 CHECK(!tunnel_interface_.empty());
Paul Stewartb26347a2013-08-02 12:12:09 -0700620 AppendOption("dev", tunnel_interface_, options);
621 AppendOption("dev-type", "tun", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100622
Darin Petkov55771b72012-04-25 09:25:19 +0200623 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100624
Paul Stewartb26347a2013-08-02 12:12:09 -0700625 AppendValueOption(kVPNMTUProperty, "mtu", options);
Ben Chan73728782013-09-20 13:40:54 -0700626 AppendValueOption(kOpenVPNProtoProperty, "proto", options);
627 AppendValueOption(kOpenVPNPortProperty, "port", options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700628 AppendValueOption(kOpenVPNTLSAuthProperty, "tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200629 {
630 string contents =
Ben Chan73728782013-09-20 13:40:54 -0700631 args()->LookupString(kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200632 if (!contents.empty()) {
Ben Chana0ddf462014-02-06 11:32:42 -0800633 if (!base::CreateTemporaryFile(&tls_auth_file_) ||
Ben Chan6fbf64f2014-05-21 18:07:01 -0700634 base::WriteFile(tls_auth_file_, contents.data(), contents.size()) !=
635 static_cast<int>(contents.size())) {
Darin Petkov1fa81942012-04-02 11:38:08 +0200636 Error::PopulateAndLog(
637 error, Error::kInternalError, "Unable to setup tls-auth file.");
638 return;
639 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700640 AppendOption("tls-auth", tls_auth_file_.value(), options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200641 }
642 }
Ben Chan73728782013-09-20 13:40:54 -0700643 AppendValueOption(kOpenVPNTLSRemoteProperty, "tls-remote", options);
644 AppendValueOption(kOpenVPNCipherProperty, "cipher", options);
645 AppendValueOption(kOpenVPNAuthProperty, "auth", options);
646 AppendFlag(kOpenVPNAuthNoCacheProperty, "auth-nocache", options);
647 AppendValueOption(kOpenVPNAuthRetryProperty, "auth-retry", options);
648 AppendFlag(kOpenVPNCompLZOProperty, "comp-lzo", options);
649 AppendFlag(kOpenVPNCompNoAdaptProperty, "comp-noadapt", options);
650 AppendFlag(kOpenVPNPushPeerInfoProperty, "push-peer-info", options);
651 AppendValueOption(kOpenVPNRenegSecProperty, "reneg-sec", options);
652 AppendValueOption(kOpenVPNShaperProperty, "shaper", options);
653 AppendValueOption(kOpenVPNServerPollTimeoutProperty,
Paul Stewartb26347a2013-08-02 12:12:09 -0700654 "server-poll-timeout", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100655
Darin Petkovca8a0e62012-09-26 13:16:52 +0200656 if (!InitCAOptions(options, error)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200657 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200658 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100659
Paul Stewart8343c0b2013-09-30 11:58:54 -0700660 // Additional remote certificate verification options.
661 InitCertificateVerifyOptions(options);
662 if (!InitExtraCertOptions(options, error)) {
663 return;
664 }
665
Darin Petkovfe6a9372012-02-28 16:25:06 +0100666 // Client-side ping support.
Paul Stewartb26347a2013-08-02 12:12:09 -0700667 AppendValueOption(kOpenVPNPingProperty, "ping", options);
668 AppendValueOption(kOpenVPNPingExitProperty, "ping-exit", options);
669 AppendValueOption(kOpenVPNPingRestartProperty, "ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100670
Ben Chan73728782013-09-20 13:40:54 -0700671 AppendValueOption(kOpenVPNNsCertTypeProperty, "ns-cert-type", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100672
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200673 InitClientAuthOptions(options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200674 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100675
676 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100677 string remote_cert_tls =
Ben Chan73728782013-09-20 13:40:54 -0700678 args()->LookupString(kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100679 if (remote_cert_tls.empty()) {
680 remote_cert_tls = "server";
681 }
682 if (remote_cert_tls != "none") {
Paul Stewartb26347a2013-08-02 12:12:09 -0700683 AppendOption("remote-cert-tls", remote_cert_tls, options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100684 }
685
686 // This is an undocumented command line argument that works like a .cfg file
Paul Stewartb26347a2013-08-02 12:12:09 -0700687 // entry. TODO(sleffler): Maybe roll this into the "tls-auth" option?
Ben Chan73728782013-09-20 13:40:54 -0700688 AppendValueOption(kOpenVPNKeyDirectionProperty, "key-direction", options);
Ben Chan73728782013-09-20 13:40:54 -0700689 AppendValueOption(kOpenVPNRemoteCertEKUProperty, "remote-cert-eku", options);
Paul Stewart18145e92014-01-16 11:30:38 -0800690 AppendDelimitedValueOption(kOpenVPNRemoteCertKUProperty,
691 "remote-cert-ku", ' ', options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100692
Darin Petkove0d5dd12012-04-04 16:10:48 +0200693 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200694 return;
695 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100696
Darin Petkova9b1fed2012-02-29 11:49:05 +0100697 // Setup openvpn-script options and RPC information required to send back
698 // Layer 3 configuration.
Paul Stewartb26347a2013-08-02 12:12:09 -0700699 AppendOption("setenv", kRPCTaskServiceVariable,
Paul Stewart406c4732013-08-01 09:30:12 -0700700 rpc_task_->GetRpcConnectionIdentifier(), options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700701 AppendOption("setenv", kRPCTaskServiceVariable,
Paul Stewart406c4732013-08-01 09:30:12 -0700702 rpc_task_->GetRpcConnectionIdentifier(), options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700703 AppendOption("setenv", kRPCTaskPathVariable, rpc_task_->GetRpcIdentifier(),
Paul Stewart406c4732013-08-01 09:30:12 -0700704 options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700705 AppendOption("script-security", "2", options);
706 AppendOption("up", kOpenVPNScript, options);
707 AppendOption("up-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100708
709 // Disable openvpn handling since we do route+ifconfig work.
Paul Stewartb26347a2013-08-02 12:12:09 -0700710 AppendOption("route-noexec", options);
711 AppendOption("ifconfig-noexec", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100712
713 // Drop root privileges on connection and enable callback scripts to send
714 // notify messages.
Paul Stewartb26347a2013-08-02 12:12:09 -0700715 AppendOption("user", "openvpn", options);
716 AppendOption("group", "openvpn", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100717}
718
Paul Stewart406c4732013-08-01 09:30:12 -0700719bool OpenVPNDriver::InitCAOptions(
720 vector<vector<string>> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200721 string ca_cert =
Ben Chan73728782013-09-20 13:40:54 -0700722 args()->LookupString(kOpenVPNCaCertProperty, "");
Paul Stewart0f9c9302013-06-14 15:41:28 -0700723 vector<string> ca_cert_pem;
724 if (args()->ContainsStrings(kOpenVPNCaCertPemProperty)) {
725 ca_cert_pem = args()->GetStrings(kOpenVPNCaCertPemProperty);
726 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700727
728 int num_ca_cert_types = 0;
729 if (!ca_cert.empty())
730 num_ca_cert_types++;
Paul Stewart5baebb72013-03-14 11:43:29 -0700731 if (!ca_cert_pem.empty())
732 num_ca_cert_types++;
733 if (num_ca_cert_types == 0) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200734 // Use default CAs if no CA certificate is provided.
Paul Stewartb26347a2013-08-02 12:12:09 -0700735 AppendOption("ca", kDefaultCACertificates, options);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200736 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700737 } else if (num_ca_cert_types > 1) {
738 Error::PopulateAndLog(
739 error, Error::kInvalidArguments,
Paul Stewartc350e682014-06-19 15:44:30 -0700740 "Can't specify more than one of CACert and CACertPEM.");
Darin Petkovca8a0e62012-09-26 13:16:52 +0200741 return false;
742 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700743 string cert_file;
Paul Stewartc350e682014-06-19 15:44:30 -0700744 if (!ca_cert_pem.empty()) {
745 DCHECK(ca_cert.empty());
Paul Stewart0f9c9302013-06-14 15:41:28 -0700746 FilePath certfile = certificate_file_->CreatePEMFromStrings(ca_cert_pem);
Paul Stewart5baebb72013-03-14 11:43:29 -0700747 if (certfile.empty()) {
748 Error::PopulateAndLog(
749 error,
750 Error::kInvalidArguments,
Paul Stewart0f9c9302013-06-14 15:41:28 -0700751 "Unable to extract PEM CA certificates.");
Paul Stewart5baebb72013-03-14 11:43:29 -0700752 return false;
753 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700754 AppendOption("ca", certfile.value(), options);
Paul Stewart5baebb72013-03-14 11:43:29 -0700755 return true;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200756 }
Paul Stewartc350e682014-06-19 15:44:30 -0700757 DCHECK(!ca_cert.empty() && ca_cert_pem.empty());
Paul Stewartb26347a2013-08-02 12:12:09 -0700758 AppendOption("ca", ca_cert, options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200759 return true;
760}
761
Paul Stewart8343c0b2013-09-30 11:58:54 -0700762void OpenVPNDriver::InitCertificateVerifyOptions(
763 std::vector<std::vector<std::string>> *options) {
764 AppendValueOption(kOpenVPNVerifyHashProperty, "verify-hash", options);
765 string x509_name = args()->LookupString(kOpenVPNVerifyX509NameProperty, "");
766 if (!x509_name.empty()) {
767 string x509_type = args()->LookupString(kOpenVPNVerifyX509TypeProperty, "");
768 if (x509_type.empty()) {
769 AppendOption("verify-x509-name", x509_name, options);
770 } else {
771 AppendOption("verify-x509-name", x509_name, x509_type, options);
772 }
773 }
774}
775
776bool OpenVPNDriver::InitExtraCertOptions(
777 vector<vector<string>> *options, Error *error) {
778 if (!args()->ContainsStrings(kOpenVPNExtraCertPemProperty)) {
779 // It's okay for this parameter to be unspecified.
780 return true;
781 }
782
783 vector<string> extra_certs = args()->GetStrings(kOpenVPNExtraCertPemProperty);
784 if (extra_certs.empty()) {
785 // It's okay for this parameter to be empty.
786 return true;
787 }
788
789 FilePath certfile =
790 extra_certificates_file_->CreatePEMFromStrings(extra_certs);
791 if (certfile.empty()) {
792 Error::PopulateAndLog(
793 error,
794 Error::kInvalidArguments,
795 "Unable to extract extra PEM CA certificates.");
796 return false;
797 }
798
799 AppendOption("extra-certs", certfile.value(), options);
800 return true;
801}
802
Paul Stewart406c4732013-08-01 09:30:12 -0700803void OpenVPNDriver::InitPKCS11Options(vector<vector<string>> *options) {
Ben Chan73728782013-09-20 13:40:54 -0700804 string id = args()->LookupString(kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200805 if (!id.empty()) {
806 string provider =
Ben Chan73728782013-09-20 13:40:54 -0700807 args()->LookupString(kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200808 if (provider.empty()) {
809 provider = kDefaultPKCS11Provider;
810 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700811 AppendOption("pkcs11-providers", provider, options);
812 AppendOption("pkcs11-id", id, options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200813 }
814}
815
Paul Stewart406c4732013-08-01 09:30:12 -0700816void OpenVPNDriver::InitClientAuthOptions(vector<vector<string>> *options) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700817 bool has_cert = AppendValueOption(kOpenVPNCertProperty, "cert", options) ||
Ben Chan73728782013-09-20 13:40:54 -0700818 !args()->LookupString(kOpenVPNClientCertIdProperty, "").empty();
Paul Stewartb26347a2013-08-02 12:12:09 -0700819 bool has_key = AppendValueOption(kOpenVPNKeyProperty, "key", options);
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200820 // If the AuthUserPass property is set, or the User property is non-empty, or
821 // there's neither a key, nor a cert available, specify user-password client
822 // authentication.
Ben Chan73728782013-09-20 13:40:54 -0700823 if (args()->ContainsString(kOpenVPNAuthUserPassProperty) ||
824 !args()->LookupString(kOpenVPNUserProperty, "").empty() ||
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200825 (!has_cert && !has_key)) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700826 AppendOption("auth-user-pass", options);
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200827 }
828}
829
Darin Petkove0d5dd12012-04-04 16:10:48 +0200830bool OpenVPNDriver::InitManagementChannelOptions(
Paul Stewart406c4732013-08-01 09:30:12 -0700831 vector<vector<string>> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200832 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200833 Error::PopulateAndLog(
834 error, Error::kInternalError, "Unable to setup management channel.");
835 return false;
836 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200837 // If there's a connected default service already, allow the openvpn client to
838 // establish connection as soon as it's started. Otherwise, hold the client
839 // until an underlying service connects and OnDefaultServiceChanged is
840 // invoked.
Peter Qiu700de642014-07-14 16:31:30 -0700841 if (manager()->IsConnected()) {
Darin Petkova5e07ef2012-07-09 14:27:57 +0200842 management_server_->ReleaseHold();
843 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200844 return true;
845}
846
Paul Stewart406c4732013-08-01 09:30:12 -0700847void OpenVPNDriver::InitLoggingOptions(vector<vector<string>> *options) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700848 AppendOption("syslog", options);
Darin Petkov55771b72012-04-25 09:25:19 +0200849
850 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
851 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
852 verb = "3";
853 }
854 if (!verb.empty()) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700855 AppendOption("verb", verb, options);
Darin Petkov55771b72012-04-25 09:25:19 +0200856 }
857}
858
Paul Stewart406c4732013-08-01 09:30:12 -0700859void OpenVPNDriver::AppendOption(
860 const string &option, vector<vector<string>> *options) {
861 options->push_back(vector<string>{ option });
862}
863
864void OpenVPNDriver::AppendOption(
865 const string &option,
866 const string &value,
867 vector<vector<string>> *options) {
868 options->push_back(vector<string>{ option, value });
869}
870
871void OpenVPNDriver::AppendOption(
872 const string &option,
873 const string &value0,
874 const string &value1,
875 vector<vector<string>> *options) {
876 options->push_back(vector<string>{ option, value0, value1 });
877}
878
Darin Petkov46463022012-03-29 14:57:32 +0200879bool OpenVPNDriver::AppendValueOption(
Paul Stewart406c4732013-08-01 09:30:12 -0700880 const string &property,
881 const string &option,
882 vector<vector<string>> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200883 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100884 if (!value.empty()) {
Paul Stewart406c4732013-08-01 09:30:12 -0700885 AppendOption(option, value, options);
Darin Petkov46463022012-03-29 14:57:32 +0200886 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100887 }
Darin Petkov46463022012-03-29 14:57:32 +0200888 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100889}
890
Paul Stewart18145e92014-01-16 11:30:38 -0800891bool OpenVPNDriver::AppendDelimitedValueOption(
892 const string &property,
893 const string &option,
894 char delimiter,
895 vector<vector<string>> *options) {
896 string value = args()->LookupString(property, "");
897 if (!value.empty()) {
898 vector<string> parts;
899 SplitString(value, delimiter, &parts);
900 parts.insert(parts.begin(), option);
901 options->push_back(parts);
902 return true;
903 }
904 return false;
905}
906
Darin Petkov46463022012-03-29 14:57:32 +0200907bool OpenVPNDriver::AppendFlag(
Paul Stewart406c4732013-08-01 09:30:12 -0700908 const string &property,
909 const string &option,
910 vector<vector<string>> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200911 if (args()->ContainsString(property)) {
Paul Stewart406c4732013-08-01 09:30:12 -0700912 AppendOption(option, options);
Darin Petkov46463022012-03-29 14:57:32 +0200913 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100914 }
Darin Petkov46463022012-03-29 14:57:32 +0200915 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100916}
917
Darin Petkov6aa21872012-03-09 16:10:19 +0100918void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700919 SLOG(VPN, 2) << __func__;
Darin Petkovaba89322013-03-11 14:48:22 +0100920 IdleService();
Darin Petkov6aa21872012-03-09 16:10:19 +0100921}
922
Darin Petkov5eb05422012-05-11 15:45:25 +0200923void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkova42afe32013-02-05 16:53:52 +0100924 LOG(INFO) << "Underlying connection disconnected.";
925 // Restart the OpenVPN client forcing a reconnect attempt.
926 management_server_->Restart();
927 // Indicate reconnect state right away to drop the VPN connection and start
928 // the connect timeout. This ensures that any miscommunication between shill
929 // and openvpn will not lead to a permanently stale connectivity state. Note
930 // that a subsequent invocation of OnReconnecting due to a RECONNECTING
931 // message will essentially be a no-op.
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100932 OnReconnecting(kReconnectReasonOffline);
Darin Petkova42afe32013-02-05 16:53:52 +0100933}
934
935void OpenVPNDriver::OnConnectTimeout() {
936 VPNDriver::OnConnectTimeout();
Darin Petkov1c049c72013-03-21 13:15:45 +0100937 Service::ConnectFailure failure =
938 management_server_->state() == OpenVPNManagementServer::kStateResolve ?
939 Service::kFailureDNSLookup : Service::kFailureConnect;
940 FailService(failure, Service::kErrorDetailsNone);
Darin Petkov5eb05422012-05-11 15:45:25 +0200941}
942
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100943void OpenVPNDriver::OnReconnecting(ReconnectReason reason) {
944 LOG(INFO) << __func__ << "(" << reason << ")";
945 int timeout_seconds = GetReconnectTimeoutSeconds(reason);
946 if (reason == kReconnectReasonTLSError &&
947 timeout_seconds < connect_timeout_seconds()) {
948 // Reconnect due to TLS error happens during connect so we need to cancel
949 // the original connect timeout first and then reduce the time limit.
950 StopConnectTimeout();
951 }
952 StartConnectTimeout(timeout_seconds);
Darin Petkova5e07ef2012-07-09 14:27:57 +0200953 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
954 // might be in hold state if the VPN connection was previously established
955 // successfully. The hold will be released by OnDefaultServiceChanged when a
956 // new default service connects. This ensures that the client will use a fully
957 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200958 if (device_) {
mukesh agrawal9da07772013-05-15 14:15:17 -0700959 device_->DropConnection();
Darin Petkov271fe522012-03-27 13:47:29 +0200960 }
961 if (service_) {
962 service_->SetState(Service::kStateAssociating);
963 }
964}
965
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100966// static
967int OpenVPNDriver::GetReconnectTimeoutSeconds(ReconnectReason reason) {
968 switch (reason) {
969 case kReconnectReasonOffline:
970 return kReconnectOfflineTimeoutSeconds;
971 case kReconnectReasonTLSError:
972 return kReconnectTLSErrorTimeoutSeconds;
973 default:
974 break;
975 }
976 return kDefaultConnectTimeoutSeconds;
977}
978
Paul Stewart39964fa2012-04-04 09:50:25 -0700979string OpenVPNDriver::GetProviderType() const {
Ben Chan73728782013-09-20 13:40:54 -0700980 return kProviderOpenVpn;
Paul Stewart39964fa2012-04-04 09:50:25 -0700981}
982
Darin Petkovb536a742012-04-26 11:31:28 +0200983KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
984 SLOG(VPN, 2) << __func__;
985 KeyValueStore props = VPNDriver::GetProvider(error);
Ben Chan73728782013-09-20 13:40:54 -0700986 props.SetBool(kPassphraseRequiredProperty,
Paul Stewartb5768232014-02-27 07:21:34 -0800987 args()->LookupString(kOpenVPNPasswordProperty, "").empty() &&
988 args()->LookupString(kOpenVPNTokenProperty, "").empty());
Darin Petkovb536a742012-04-26 11:31:28 +0200989 return props;
990}
991
Darin Petkov1a462de2012-05-02 11:10:48 +0200992// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
993// singleton if it's used outside OpenVPN.
994bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
995 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
996 string contents;
Ben Chana0ddf462014-02-06 11:32:42 -0800997 if (!base::ReadFileToString(lsb_release_file_, &contents)) {
Darin Petkov1a462de2012-05-02 11:10:48 +0200998 LOG(ERROR) << "Unable to read the lsb-release file: "
999 << lsb_release_file_.value();
1000 return false;
1001 }
1002 vector<string> lines;
1003 SplitString(contents, '\n', &lines);
Paul Stewart406c4732013-08-01 09:30:12 -07001004 for (const auto &line : lines) {
1005 size_t assign = line.find('=');
Darin Petkov1a462de2012-05-02 11:10:48 +02001006 if (assign == string::npos) {
1007 continue;
1008 }
Paul Stewart406c4732013-08-01 09:30:12 -07001009 (*lsb_release)[line.substr(0, assign)] = line.substr(assign + 1);
Darin Petkov1a462de2012-05-02 11:10:48 +02001010 }
1011 return true;
1012}
1013
1014void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
1015 // Adds the platform name and version to the environment so that openvpn can
1016 // send them to the server when OpenVPN.PushPeerInfo is set.
1017 map<string, string> lsb_release;
1018 ParseLSBRelease(&lsb_release);
1019 string platform_name = lsb_release[kChromeOSReleaseName];
1020 if (!platform_name.empty()) {
1021 environment->push_back("IV_PLAT=" + platform_name);
1022 }
1023 string platform_version = lsb_release[kChromeOSReleaseVersion];
1024 if (!platform_version.empty()) {
1025 environment->push_back("IV_PLAT_REL=" + platform_version);
1026 }
1027}
1028
Darin Petkova5e07ef2012-07-09 14:27:57 +02001029void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
1030 SLOG(VPN, 2) << __func__
Darin Petkov457728b2013-01-09 09:49:08 +01001031 << "(" << (service ? service->unique_name() : "-") << ")";
Darin Petkova5e07ef2012-07-09 14:27:57 +02001032 // Allow the openvpn client to connect/reconnect only over a connected
1033 // underlying default service. If there's no default connected service, hold
1034 // the openvpn client until an underlying connection is established. If the
1035 // default service is our VPN service, hold the openvpn client on reconnect so
1036 // that the VPN connection can be torn down fully before a new connection
1037 // attempt is made over the underlying service.
1038 if (service && service != service_ && service->IsConnected()) {
1039 management_server_->ReleaseHold();
1040 } else {
1041 management_server_->Hold();
1042 }
1043}
1044
Paul Stewart91a43cb2013-03-02 21:34:15 -08001045void OpenVPNDriver::ReportConnectionMetrics() {
1046 metrics_->SendEnumToUMA(
1047 Metrics::kMetricVpnDriver,
1048 Metrics::kVpnDriverOpenVpn,
1049 Metrics::kMetricVpnDriverMax);
1050
Paul Stewartc350e682014-06-19 15:44:30 -07001051 if (args()->LookupString(kOpenVPNCaCertProperty, "") != "" ||
Paul Stewartb5768232014-02-27 07:21:34 -08001052 (args()->ContainsStrings(kOpenVPNCaCertPemProperty) &&
1053 !args()->GetStrings(kOpenVPNCaCertPemProperty).empty())) {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001054 metrics_->SendEnumToUMA(
1055 Metrics::kMetricVpnRemoteAuthenticationType,
1056 Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
1057 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1058 } else {
1059 metrics_->SendEnumToUMA(
1060 Metrics::kMetricVpnRemoteAuthenticationType,
1061 Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
1062 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1063 }
1064
1065 bool has_user_authentication = false;
Paul Stewartb5768232014-02-27 07:21:34 -08001066 if (args()->LookupString(kOpenVPNTokenProperty, "") != "") {
1067 metrics_->SendEnumToUMA(
1068 Metrics::kMetricVpnUserAuthenticationType,
1069 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernameToken,
1070 Metrics::kMetricVpnUserAuthenticationTypeMax);
1071 has_user_authentication = true;
1072 }
Ben Chan73728782013-09-20 13:40:54 -07001073 if (args()->LookupString(kOpenVPNOTPProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001074 metrics_->SendEnumToUMA(
1075 Metrics::kMetricVpnUserAuthenticationType,
1076 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
1077 Metrics::kMetricVpnUserAuthenticationTypeMax);
1078 has_user_authentication = true;
1079 }
Ben Chan73728782013-09-20 13:40:54 -07001080 if (args()->LookupString(kOpenVPNAuthUserPassProperty, "") != "" ||
1081 args()->LookupString(kOpenVPNUserProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001082 metrics_->SendEnumToUMA(
1083 Metrics::kMetricVpnUserAuthenticationType,
1084 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
1085 Metrics::kMetricVpnUserAuthenticationTypeMax);
1086 has_user_authentication = true;
1087 }
Ben Chan73728782013-09-20 13:40:54 -07001088 if (args()->LookupString(kOpenVPNClientCertIdProperty, "") != "" ||
Paul Stewarte8e71da2013-03-20 08:48:33 -07001089 args()->LookupString(kOpenVPNCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001090 metrics_->SendEnumToUMA(
1091 Metrics::kMetricVpnUserAuthenticationType,
1092 Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
1093 Metrics::kMetricVpnUserAuthenticationTypeMax);
1094 has_user_authentication = true;
1095 }
1096 if (!has_user_authentication) {
1097 metrics_->SendEnumToUMA(
1098 Metrics::kMetricVpnUserAuthenticationType,
1099 Metrics::kVpnUserAuthenticationTypeOpenVpnNone,
1100 Metrics::kMetricVpnUserAuthenticationTypeMax);
1101 }
1102}
1103
Darin Petkov33af05c2012-02-28 10:10:30 +01001104} // namespace shill