blob: c2af24361f6bda127cc595cb256cf2305fbbbe0d [file] [log] [blame]
Darin Petkov33af05c2012-02-28 10:10:30 +01001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/openvpn_driver.h"
6
Darin Petkov14c29ec2012-03-02 11:34:19 +01007#include <arpa/inet.h>
8
Darin Petkov1fa81942012-04-02 11:38:08 +02009#include <base/file_util.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010010#include <base/string_number_conversions.h>
Darin Petkov78f63262012-03-26 01:30:24 +020011#include <base/string_split.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010012#include <base/string_util.h>
Darin Petkovfe6a9372012-02-28 16:25:06 +010013#include <chromeos/dbus/service_constants.h>
14
Paul Stewart5baebb72013-03-14 11:43:29 -070015#include "shill/certificate_file.h"
Paul Stewartce4ec192012-03-14 12:53:46 -070016#include "shill/connection.h"
Paul Stewartca6abd42012-03-01 15:45:29 -080017#include "shill/device_info.h"
Darin Petkov14c29ec2012-03-02 11:34:19 +010018#include "shill/dhcp_config.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010019#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070020#include "shill/logging.h"
Paul Stewartce4ec192012-03-14 12:53:46 -070021#include "shill/manager.h"
Darin Petkov3c5e4dc2012-04-02 14:44:27 +020022#include "shill/nss.h"
Darin Petkov46463022012-03-29 14:57:32 +020023#include "shill/openvpn_management_server.h"
Darin Petkov5a850472012-06-06 15:44:24 +020024#include "shill/process_killer.h"
Darin Petkova9b1fed2012-02-29 11:49:05 +010025#include "shill/rpc_task.h"
Darin Petkov46463022012-03-29 14:57:32 +020026#include "shill/sockets.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070027#include "shill/virtual_device.h"
Darin Petkov79d74c92012-03-07 17:20:32 +010028#include "shill/vpn_service.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010029
Darin Petkov5a850472012-06-06 15:44:24 +020030using base::Closure;
Albert Chaulk0e1cdea2013-02-27 15:32:55 -080031using base::FilePath;
Darin Petkov78f63262012-03-26 01:30:24 +020032using base::SplitString;
Darin Petkova5e07ef2012-07-09 14:27:57 +020033using base::Unretained;
Darin Petkov5a850472012-06-06 15:44:24 +020034using base::WeakPtr;
Darin Petkov14c29ec2012-03-02 11:34:19 +010035using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010036using std::string;
37using std::vector;
38
Darin Petkov33af05c2012-02-28 10:10:30 +010039namespace shill {
40
Darin Petkova9b1fed2012-02-29 11:49:05 +010041namespace {
Darin Petkov5a850472012-06-06 15:44:24 +020042
Darin Petkov14c29ec2012-03-02 11:34:19 +010043const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
44const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
45const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
46const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
47const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
48const char kOpenVPNRouteOptionPrefix[] = "route_";
49const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
50const char kOpenVPNTrustedIP[] = "trusted_ip";
51const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010052
Darin Petkove0d5dd12012-04-04 16:10:48 +020053const char kDefaultPKCS11Provider[] = "libchaps.so";
54
Paul Stewart4698c1a2013-05-16 15:42:19 -070055// Some configurations pass the netmask in the ifconfig_remote property.
56// This is due to some servers not explicitly indicating that they are using
57// a "broadcast mode" network instead of peer-to-peer. See
58// http://crbug.com/241264 for an example of this issue.
59const char kSuspectedNetmaskPrefix[] = "255.";
60
Paul Stewartebd38562012-03-23 13:06:40 -070061} // namespace
62
63// static
Darin Petkovc418b4b2012-10-05 11:42:52 +020064const char OpenVPNDriver::kDefaultCACertificates[] =
65 "/etc/ssl/certs/ca-certificates.crt";
Darin Petkovca8a0e62012-09-26 13:16:52 +020066// static
Paul Stewartebd38562012-03-23 13:06:40 -070067const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
68// static
Darin Petkov728faa92012-10-12 11:25:47 +020069const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
Paul Stewartebd38562012-03-23 13:06:40 -070070// static
Darin Petkovd4325392012-04-23 15:48:22 +020071const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020072 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
73 { flimflam::kOpenVPNAuthProperty, 0 },
74 { flimflam::kOpenVPNAuthRetryProperty, 0 },
75 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
76 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
77 { flimflam::kOpenVPNCaCertProperty, 0 },
78 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020079 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020080 { flimflam::kOpenVPNCompLZOProperty, 0 },
81 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
82 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
83 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020084 { flimflam::kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020085 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
86 { flimflam::kOpenVPNPasswordProperty,
87 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020088 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020089 { flimflam::kOpenVPNPortProperty, 0 },
90 { flimflam::kOpenVPNProtoProperty, 0 },
91 { flimflam::kOpenVPNProviderProperty, 0 },
92 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
93 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
94 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
95 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
96 { flimflam::kOpenVPNRenegSecProperty, 0 },
97 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
98 { flimflam::kOpenVPNShaperProperty, 0 },
99 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
100 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
101 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
102 { flimflam::kOpenVPNUserProperty, 0 },
103 { flimflam::kProviderHostProperty, 0 },
Darin Petkov1847d792012-04-17 11:33:06 +0200104 { flimflam::kProviderTypeProperty, 0 },
Paul Stewart0f9c9302013-06-14 15:41:28 -0700105 { kOpenVPNCaCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200106 { kOpenVPNCertProperty, 0 },
Paul Stewartf3b6d572013-06-17 09:35:33 -0700107 { kOpenVPNExtraCertPemProperty, Property::kArray },
Darin Petkov1847d792012-04-17 11:33:06 +0200108 { kOpenVPNKeyProperty, 0 },
109 { kOpenVPNPingExitProperty, 0 },
110 { kOpenVPNPingProperty, 0 },
111 { kOpenVPNPingRestartProperty, 0 },
112 { kOpenVPNTLSAuthProperty, 0 },
113 { kOpenVPNVerbProperty, 0 },
114 { kVPNMTUProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100115};
Paul Stewart291a4732012-03-14 19:19:02 -0700116
Darin Petkov1a462de2012-05-02 11:10:48 +0200117const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
Darin Petkov1a462de2012-05-02 11:10:48 +0200118const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
Darin Petkov1a462de2012-05-02 11:10:48 +0200119const char OpenVPNDriver::kChromeOSReleaseVersion[] =
120 "CHROMEOS_RELEASE_VERSION";
Paul Stewartb26347a2013-08-02 12:12:09 -0700121
122// Directory where OpenVPN configuration files are exported while the
123// process is running.
124const char OpenVPNDriver::kDefaultOpenVPNConfigurationDirectory[] =
125 RUNDIR "/openvpn_config";
126
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100127const int OpenVPNDriver::kReconnectOfflineTimeoutSeconds = 2 * 60;
128const int OpenVPNDriver::kReconnectTLSErrorTimeoutSeconds = 20;
Darin Petkov1a462de2012-05-02 11:10:48 +0200129
Darin Petkova9b1fed2012-02-29 11:49:05 +0100130OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100131 EventDispatcher *dispatcher,
132 Metrics *metrics,
133 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800134 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700135 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +0200136 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200137 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100138 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800139 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100140 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200141 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200142 nss_(NSS::GetInstance()),
Paul Stewarteb713e82013-06-28 14:51:54 -0700143 certificate_file_(new CertificateFile()),
Darin Petkov5a850472012-06-06 15:44:24 +0200144 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200145 lsb_release_file_(kLSBReleaseFile),
Paul Stewartb26347a2013-08-02 12:12:09 -0700146 openvpn_config_directory_(kDefaultOpenVPNConfigurationDirectory),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100147 pid_(0),
Darin Petkova5e07ef2012-07-09 14:27:57 +0200148 child_watch_tag_(0),
149 default_service_callback_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100150
Darin Petkov36a3ace2012-03-06 17:22:14 +0100151OpenVPNDriver::~OpenVPNDriver() {
Darin Petkovaba89322013-03-11 14:48:22 +0100152 IdleService();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100153}
154
Darin Petkovaba89322013-03-11 14:48:22 +0100155void OpenVPNDriver::IdleService() {
Darin Petkov1c049c72013-03-21 13:15:45 +0100156 Cleanup(Service::kStateIdle,
157 Service::kFailureUnknown,
158 Service::kErrorDetailsNone);
Darin Petkovaba89322013-03-11 14:48:22 +0100159}
160
Darin Petkov1c049c72013-03-21 13:15:45 +0100161void OpenVPNDriver::FailService(Service::ConnectFailure failure,
162 const string &error_details) {
163 Cleanup(Service::kStateFailure, failure, error_details);
Darin Petkovaba89322013-03-11 14:48:22 +0100164}
165
166void OpenVPNDriver::Cleanup(Service::ConnectState state,
Darin Petkov1c049c72013-03-21 13:15:45 +0100167 Service::ConnectFailure failure,
Darin Petkovaba89322013-03-11 14:48:22 +0100168 const string &error_details) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700169 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
Darin Petkovaba89322013-03-11 14:48:22 +0100170 << ", " << error_details << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200171 StopConnectTimeout();
Darin Petkov84a95532013-03-13 12:24:45 +0100172 if (child_watch_tag_) {
173 glib_->SourceRemove(child_watch_tag_);
174 child_watch_tag_ = 0;
175 }
176 // Disconnecting the management interface will terminate the openvpn
177 // process. Ensure this is handled robustly by first removing the child watch
178 // above and then terminating and reaping the process through ProcessKiller.
Darin Petkov46463022012-03-29 14:57:32 +0200179 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200180 if (!tls_auth_file_.empty()) {
181 file_util::Delete(tls_auth_file_, false);
182 tls_auth_file_.clear();
183 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700184 if (!openvpn_config_file_.empty()) {
185 file_util::Delete(openvpn_config_file_, false);
186 openvpn_config_file_.clear();
187 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200188 if (default_service_callback_tag_) {
189 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
190 default_service_callback_tag_ = 0;
191 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100192 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200193 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100194 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200195 interface_index = device_->interface_index();
mukesh agrawal9da07772013-05-15 14:15:17 -0700196 device_->DropConnection();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500197 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100198 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200199 }
200 if (pid_) {
201 Closure callback;
202 if (interface_index >= 0) {
203 callback =
204 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
205 interface_index = -1;
206 }
207 process_killer_->Kill(pid_, callback);
208 pid_ = 0;
209 }
210 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100211 device_info_->DeleteInterface(interface_index);
212 }
213 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100214 if (service_) {
Darin Petkov1c049c72013-03-21 13:15:45 +0100215 if (state == Service::kStateFailure) {
216 service_->SetErrorDetails(error_details);
217 service_->SetFailure(failure);
218 } else {
219 service_->SetState(state);
220 }
Darin Petkov79d74c92012-03-07 17:20:32 +0100221 service_ = NULL;
222 }
Darin Petkov3189a472012-10-05 09:55:33 +0200223 ip_properties_ = IPConfig::Properties();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100224}
225
Paul Stewart406c4732013-08-01 09:30:12 -0700226// static
Paul Stewartb26347a2013-08-02 12:12:09 -0700227string OpenVPNDriver::JoinOptions(const vector<vector<string>> &options,
228 char separator) {
Paul Stewart406c4732013-08-01 09:30:12 -0700229 vector<string> option_strings;
230 for (const auto &option : options) {
Paul Stewart7590d6f2013-08-07 12:47:39 -0700231 vector<string> quoted_option;
232 for (const auto &argument : option) {
233 if (argument.find(' ') != string::npos ||
234 argument.find('\t') != string::npos ||
235 argument.find('"') != string::npos ||
236 argument.find(separator) != string::npos) {
237 string quoted_argument(argument);
238 const char separator_chars[] = { separator, '\0' };
239 ReplaceChars(argument, separator_chars, " ", &quoted_argument);
240 ReplaceChars(quoted_argument, "\\", "\\\\", &quoted_argument);
241 ReplaceChars(quoted_argument, "\"", "\\\"", &quoted_argument);
242 quoted_option.push_back("\"" + quoted_argument + "\"");
243 } else {
244 quoted_option.push_back(argument);
245 }
246 }
247 option_strings.push_back(JoinString(quoted_option, ' '));
Paul Stewart406c4732013-08-01 09:30:12 -0700248 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700249 return JoinString(option_strings, separator);
250}
251
252bool OpenVPNDriver::WriteConfigFile(
253 const vector<vector<string>> &options,
254 FilePath *config_file) {
255 if (!file_util::DirectoryExists(openvpn_config_directory_)) {
256 if (!file_util::CreateDirectory(openvpn_config_directory_)) {
257 LOG(ERROR) << "Unable to create configuration directory "
258 << openvpn_config_directory_.value();
259 return false;
260 }
261 if (chmod(openvpn_config_directory_.value().c_str(), S_IRWXU)) {
262 LOG(ERROR) << "Failed to set permissions on "
263 << openvpn_config_directory_.value();
264 file_util::Delete(openvpn_config_directory_, true);
265 return false;
266 }
267 }
268
269 string contents = JoinOptions(options, '\n');
270 contents.push_back('\n');
271 if (!file_util::CreateTemporaryFileInDir(openvpn_config_directory_,
272 config_file) ||
273 file_util::WriteFile(*config_file, contents.data(), contents.size()) !=
274 static_cast<int>(contents.size())) {
275 LOG(ERROR) << "Unable to setup OpenVPN config file.";
276 return false;
277 }
278 return true;
Paul Stewart406c4732013-08-01 09:30:12 -0700279}
280
Darin Petkov36a3ace2012-03-06 17:22:14 +0100281bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700282 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100283
Paul Stewart406c4732013-08-01 09:30:12 -0700284 vector<vector<string>> options;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100285 Error error;
286 InitOptions(&options, &error);
287 if (error.IsFailure()) {
288 return false;
289 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700290 LOG(INFO) << "OpenVPN process options: " << JoinOptions(options, ',');
291 if (!WriteConfigFile(options, &openvpn_config_file_)) {
292 return false;
293 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100294
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700295 // TODO(quiche): This should be migrated to use ExternalTask.
296 // (crbug.com/246263).
Darin Petkov36a3ace2012-03-06 17:22:14 +0100297 vector<char *> process_args;
298 process_args.push_back(const_cast<char *>(kOpenVPNPath));
Paul Stewartb26347a2013-08-02 12:12:09 -0700299 process_args.push_back(const_cast<char *>("--config"));
300 process_args.push_back(const_cast<char *>(
301 openvpn_config_file_.value().c_str()));
Darin Petkov36a3ace2012-03-06 17:22:14 +0100302 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200303
304 vector<string> environment;
305 InitEnvironment(&environment);
306
307 vector<char *> process_env;
Paul Stewart406c4732013-08-01 09:30:12 -0700308 for (const auto &environment_variable : environment) {
309 process_env.push_back(const_cast<char *>(environment_variable.c_str()));
Darin Petkov1a462de2012-05-02 11:10:48 +0200310 }
311 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100312
313 CHECK(!pid_);
Darin Petkov68710d72013-02-13 14:22:56 +0100314 if (!glib_->SpawnAsync(NULL,
315 process_args.data(),
316 process_env.data(),
317 G_SPAWN_DO_NOT_REAP_CHILD,
318 NULL,
319 NULL,
320 &pid_,
321 NULL)) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100322 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
323 return false;
324 }
325 CHECK(!child_watch_tag_);
326 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
327 return true;
328}
329
330// static
331void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700332 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100333 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
334 me->child_watch_tag_ = 0;
335 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200336 me->pid_ = 0;
Darin Petkov1c049c72013-03-21 13:15:45 +0100337 me->FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100338 // TODO(petkov): Figure if we need to restart the connection.
339}
Darin Petkov33af05c2012-02-28 10:10:30 +0100340
Darin Petkov5a850472012-06-06 15:44:24 +0200341// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200342void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200343 int interface_index) {
344 if (device_info) {
345 LOG(INFO) << "Deleting interface " << interface_index;
346 device_info->DeleteInterface(interface_index);
347 }
348}
349
Paul Stewartca6abd42012-03-01 15:45:29 -0800350bool OpenVPNDriver::ClaimInterface(const string &link_name,
351 int interface_index) {
352 if (link_name != tunnel_interface_) {
353 return false;
354 }
355
Ben Chanfad4a0b2012-04-18 15:49:59 -0700356 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800357
Darin Petkovf20994f2012-03-05 16:12:19 +0100358 CHECK(!device_);
mukesh agrawal9da07772013-05-15 14:15:17 -0700359 device_ = new VirtualDevice(control_, dispatcher(), metrics_, manager(),
360 link_name, interface_index, Technology::kVPN);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500361 device_->SetEnabled(true);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500362
Darin Petkov36a3ace2012-03-06 17:22:14 +0100363 rpc_task_.reset(new RPCTask(control_, this));
Darin Petkov1c049c72013-03-21 13:15:45 +0100364 if (SpawnOpenVPN()) {
Darin Petkov68710d72013-02-13 14:22:56 +0100365 default_service_callback_tag_ =
366 manager()->RegisterDefaultServiceCallback(
367 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Darin Petkov1c049c72013-03-21 13:15:45 +0100368 } else {
369 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100370 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800371 return true;
372}
373
Darin Petkov209e6292012-04-20 11:33:32 +0200374void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
375 NOTREACHED();
376}
377
Darin Petkov36a3ace2012-03-06 17:22:14 +0100378void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100379 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200380 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100381 if (reason != "up") {
mukesh agrawal9da07772013-05-15 14:15:17 -0700382 device_->DropConnection();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100383 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100384 }
Darin Petkov3189a472012-10-05 09:55:33 +0200385 // On restart/reconnect, update the existing IP configuration.
386 ParseIPConfiguration(dict, &ip_properties_);
387 device_->SelectService(service_);
388 device_->UpdateIPConfig(ip_properties_);
Paul Stewart91a43cb2013-03-02 21:34:15 -0800389 ReportConnectionMetrics();
Darin Petkov602303f2012-06-06 12:15:59 +0200390 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100391}
392
393// static
394void OpenVPNDriver::ParseIPConfiguration(
395 const map<string, string> &configuration,
396 IPConfig::Properties *properties) {
397 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100398 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100399 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200400 if (!properties->subnet_prefix) {
401 properties->subnet_prefix =
402 IPAddress::GetMaxPrefixLength(properties->address_family);
403 }
Paul Stewart406c4732013-08-01 09:30:12 -0700404 for (const auto &configuration_map : configuration) {
405 const string &key = configuration_map.first;
406 const string &value = configuration_map.second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700407 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100408 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
409 properties->address = value;
410 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
411 properties->broadcast_address = value;
412 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700413 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100414 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
415 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
Paul Stewart4698c1a2013-05-16 15:42:19 -0700416 if (StartsWithASCII(value, kSuspectedNetmaskPrefix, false)) {
417 LOG(WARNING) << "Option " << key << " value " << value
418 << " looks more like a netmask than a peer address; "
419 << "assuming it is the former.";
420 // In this situation, the "peer_address" value will be left
421 // unset and Connection::UpdateFromIPConfig() will treat the
422 // interface as if it were a broadcast-style network. The
423 // kernel will, automatically set the peer address equal to
424 // the local address.
425 properties->subnet_prefix =
426 IPAddress::GetPrefixLengthFromMask(properties->address_family,
427 value);
428 } else {
429 properties->peer_address = value;
430 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100431 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
432 properties->gateway = value;
433 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700434 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100435 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
436 int mtu = 0;
437 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
438 properties->mtu = mtu;
439 } else {
440 LOG(ERROR) << "MTU " << value << " ignored.";
441 }
442 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
443 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
444 int order = 0;
445 if (base::StringToInt(suffix, &order)) {
446 foreign_options[order] = value;
447 } else {
448 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
449 }
450 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100451 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
452 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100453 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700454 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100455 }
456 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100457 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100458 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100459}
460
461// static
462void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
463 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200464 vector<string> domain_search;
465 vector<string> dns_servers;
Paul Stewart406c4732013-08-01 09:30:12 -0700466 for (const auto &option_map : options) {
467 ParseForeignOption(option_map.second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100468 }
Darin Petkove8587e32012-07-02 13:56:07 +0200469 if (!domain_search.empty()) {
470 properties->domain_search.swap(domain_search);
471 }
472 LOG_IF(WARNING, properties->domain_search.empty())
473 << "No search domains provided.";
474 if (!dns_servers.empty()) {
475 properties->dns_servers.swap(dns_servers);
476 }
477 LOG_IF(WARNING, properties->dns_servers.empty())
478 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100479}
480
481// static
482void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200483 vector<string> *domain_search,
484 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700485 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100486 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200487 SplitString(option, ' ', &tokens);
488 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100489 return;
490 }
491 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200492 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100493 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200494 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100495 }
496}
497
Darin Petkov60596742012-03-05 12:17:17 +0100498// static
499IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
500 const string &prefix, const string &key, RouteOptions *routes) {
501 int order = 0;
502 if (!StartsWithASCII(key, prefix, false) ||
503 !base::StringToInt(key.substr(prefix.size()), &order)) {
504 return NULL;
505 }
506 return &(*routes)[order];
507}
508
509// static
510void OpenVPNDriver::ParseRouteOption(
511 const string &key, const string &value, RouteOptions *routes) {
512 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
513 if (route) {
514 route->host = value;
515 return;
516 }
517 route = GetRouteOptionEntry("netmask_", key, routes);
518 if (route) {
519 route->netmask = value;
520 return;
521 }
522 route = GetRouteOptionEntry("gateway_", key, routes);
523 if (route) {
524 route->gateway = value;
525 return;
526 }
527 LOG(WARNING) << "Unknown route option ignored: " << key;
528}
529
530// static
531void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
532 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200533 vector<IPConfig::Route> new_routes;
Paul Stewart406c4732013-08-01 09:30:12 -0700534 for (const auto &route_map : routes) {
535 const IPConfig::Route &route = route_map.second;
Darin Petkov60596742012-03-05 12:17:17 +0100536 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
Paul Stewart406c4732013-08-01 09:30:12 -0700537 LOG(WARNING) << "Ignoring incomplete route: " << route_map.first;
Darin Petkov60596742012-03-05 12:17:17 +0100538 continue;
539 }
Darin Petkove8587e32012-07-02 13:56:07 +0200540 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100541 }
Darin Petkove8587e32012-07-02 13:56:07 +0200542 if (!new_routes.empty()) {
543 properties->routes.swap(new_routes);
544 }
545 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100546}
547
Darin Petkov4b944842012-09-21 10:48:48 +0200548// static
549bool OpenVPNDriver::SplitPortFromHost(
550 const string &host, string *name, string *port) {
551 vector<string> tokens;
552 SplitString(host, ':', &tokens);
553 int port_number = 0;
554 if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
555 !IsAsciiDigit(tokens[1][0]) ||
556 !base::StringToInt(tokens[1], &port_number) || port_number > kuint16max) {
557 return false;
558 }
559 *name = tokens[0];
560 *port = tokens[1];
561 return true;
562}
563
Darin Petkov602303f2012-06-06 12:15:59 +0200564void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100565 StartConnectTimeout(kDefaultConnectTimeoutSeconds);
Darin Petkov79d74c92012-03-07 17:20:32 +0100566 service_ = service;
567 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100568 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
569 Error::PopulateAndLog(
570 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov1c049c72013-03-21 13:15:45 +0100571 FailService(Service::kFailureInternal, Service::kErrorDetailsNone);
Darin Petkovf20994f2012-03-05 16:12:19 +0100572 }
573 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100574}
575
Paul Stewart406c4732013-08-01 09:30:12 -0700576void OpenVPNDriver::InitOptions(vector<vector<string>> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200577 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100578 if (vpnhost.empty()) {
579 Error::PopulateAndLog(
580 error, Error::kInvalidArguments, "VPN host not specified.");
581 return;
582 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700583 AppendOption("client", options);
584 AppendOption("tls-client", options);
Darin Petkov4b944842012-09-21 10:48:48 +0200585
Darin Petkov4b944842012-09-21 10:48:48 +0200586 string host_name, host_port;
587 if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
588 DCHECK(!host_name.empty());
589 DCHECK(!host_port.empty());
Paul Stewartb26347a2013-08-02 12:12:09 -0700590 AppendOption("remote", host_name, host_port, options);
Darin Petkov4b944842012-09-21 10:48:48 +0200591 } else {
Paul Stewartb26347a2013-08-02 12:12:09 -0700592 AppendOption("remote", vpnhost, options);
Darin Petkov4b944842012-09-21 10:48:48 +0200593 }
594
Paul Stewartb26347a2013-08-02 12:12:09 -0700595 AppendOption("nobind", options);
596 AppendOption("persist-key", options);
597 AppendOption("persist-tun", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100598
Darin Petkovf20994f2012-03-05 16:12:19 +0100599 CHECK(!tunnel_interface_.empty());
Paul Stewartb26347a2013-08-02 12:12:09 -0700600 AppendOption("dev", tunnel_interface_, options);
601 AppendOption("dev-type", "tun", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100602
Darin Petkov55771b72012-04-25 09:25:19 +0200603 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100604
Paul Stewartb26347a2013-08-02 12:12:09 -0700605 AppendValueOption(kVPNMTUProperty, "mtu", options);
606 AppendValueOption(flimflam::kOpenVPNProtoProperty, "proto", options);
607 AppendValueOption(flimflam::kOpenVPNPortProperty, "port", options);
608 AppendValueOption(kOpenVPNTLSAuthProperty, "tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200609 {
610 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200611 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200612 if (!contents.empty()) {
613 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
614 file_util::WriteFile(
615 tls_auth_file_, contents.data(), contents.size()) !=
616 static_cast<int>(contents.size())) {
617 Error::PopulateAndLog(
618 error, Error::kInternalError, "Unable to setup tls-auth file.");
619 return;
620 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700621 AppendOption("tls-auth", tls_auth_file_.value(), options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200622 }
623 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100624 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700625 flimflam::kOpenVPNTLSRemoteProperty, "tls-remote", options);
626 AppendValueOption(flimflam::kOpenVPNCipherProperty, "cipher", options);
627 AppendValueOption(flimflam::kOpenVPNAuthProperty, "auth", options);
628 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "auth-nocache", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100629 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700630 flimflam::kOpenVPNAuthRetryProperty, "auth-retry", options);
631 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "comp-lzo", options);
632 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "comp-noadapt", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100633 AppendFlag(
Paul Stewartb26347a2013-08-02 12:12:09 -0700634 flimflam::kOpenVPNPushPeerInfoProperty, "push-peer-info", options);
635 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "reneg-sec", options);
636 AppendValueOption(flimflam::kOpenVPNShaperProperty, "shaper", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100637 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
Paul Stewartb26347a2013-08-02 12:12:09 -0700638 "server-poll-timeout", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100639
Darin Petkovca8a0e62012-09-26 13:16:52 +0200640 if (!InitCAOptions(options, error)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200641 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200642 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100643
644 // Client-side ping support.
Paul Stewartb26347a2013-08-02 12:12:09 -0700645 AppendValueOption(kOpenVPNPingProperty, "ping", options);
646 AppendValueOption(kOpenVPNPingExitProperty, "ping-exit", options);
647 AppendValueOption(kOpenVPNPingRestartProperty, "ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100648
Darin Petkovfe6a9372012-02-28 16:25:06 +0100649 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700650 flimflam::kOpenVPNNsCertTypeProperty, "ns-cert-type", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100651
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200652 InitClientAuthOptions(options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200653 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100654
655 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100656 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200657 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100658 if (remote_cert_tls.empty()) {
659 remote_cert_tls = "server";
660 }
661 if (remote_cert_tls != "none") {
Paul Stewartb26347a2013-08-02 12:12:09 -0700662 AppendOption("remote-cert-tls", remote_cert_tls, options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100663 }
664
665 // This is an undocumented command line argument that works like a .cfg file
Paul Stewartb26347a2013-08-02 12:12:09 -0700666 // entry. TODO(sleffler): Maybe roll this into the "tls-auth" option?
Darin Petkovfe6a9372012-02-28 16:25:06 +0100667 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700668 flimflam::kOpenVPNKeyDirectionProperty, "key-direction", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100669 // TODO(sleffler): Support more than one eku parameter.
670 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700671 flimflam::kOpenVPNRemoteCertEKUProperty, "remote-cert-eku", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100672 AppendValueOption(
Paul Stewartb26347a2013-08-02 12:12:09 -0700673 flimflam::kOpenVPNRemoteCertKUProperty, "remote-cert-ku", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100674
Darin Petkove0d5dd12012-04-04 16:10:48 +0200675 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200676 return;
677 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100678
Darin Petkova9b1fed2012-02-29 11:49:05 +0100679 // Setup openvpn-script options and RPC information required to send back
680 // Layer 3 configuration.
Paul Stewartb26347a2013-08-02 12:12:09 -0700681 AppendOption("setenv", kRPCTaskServiceVariable,
Paul Stewart406c4732013-08-01 09:30:12 -0700682 rpc_task_->GetRpcConnectionIdentifier(), options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700683 AppendOption("setenv", kRPCTaskServiceVariable,
Paul Stewart406c4732013-08-01 09:30:12 -0700684 rpc_task_->GetRpcConnectionIdentifier(), options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700685 AppendOption("setenv", kRPCTaskPathVariable, rpc_task_->GetRpcIdentifier(),
Paul Stewart406c4732013-08-01 09:30:12 -0700686 options);
Paul Stewartb26347a2013-08-02 12:12:09 -0700687 AppendOption("script-security", "2", options);
688 AppendOption("up", kOpenVPNScript, options);
689 AppendOption("up-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100690
691 // Disable openvpn handling since we do route+ifconfig work.
Paul Stewartb26347a2013-08-02 12:12:09 -0700692 AppendOption("route-noexec", options);
693 AppendOption("ifconfig-noexec", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100694
695 // Drop root privileges on connection and enable callback scripts to send
696 // notify messages.
Paul Stewartb26347a2013-08-02 12:12:09 -0700697 AppendOption("user", "openvpn", options);
698 AppendOption("group", "openvpn", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100699}
700
Paul Stewart406c4732013-08-01 09:30:12 -0700701bool OpenVPNDriver::InitCAOptions(
702 vector<vector<string>> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200703 string ca_cert =
Darin Petkovca8a0e62012-09-26 13:16:52 +0200704 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "");
705 string ca_cert_nss =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200706 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Paul Stewart0f9c9302013-06-14 15:41:28 -0700707 vector<string> ca_cert_pem;
708 if (args()->ContainsStrings(kOpenVPNCaCertPemProperty)) {
709 ca_cert_pem = args()->GetStrings(kOpenVPNCaCertPemProperty);
710 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700711
712 int num_ca_cert_types = 0;
713 if (!ca_cert.empty())
714 num_ca_cert_types++;
715 if (!ca_cert_nss.empty())
716 num_ca_cert_types++;
717 if (!ca_cert_pem.empty())
718 num_ca_cert_types++;
719 if (num_ca_cert_types == 0) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200720 // Use default CAs if no CA certificate is provided.
Paul Stewartb26347a2013-08-02 12:12:09 -0700721 AppendOption("ca", kDefaultCACertificates, options);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200722 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700723 } else if (num_ca_cert_types > 1) {
724 Error::PopulateAndLog(
725 error, Error::kInvalidArguments,
726 "Can't specify more than one of CACert, CACertNSS and CACertPEM.");
Darin Petkovca8a0e62012-09-26 13:16:52 +0200727 return false;
728 }
Paul Stewart5baebb72013-03-14 11:43:29 -0700729 string cert_file;
Darin Petkovca8a0e62012-09-26 13:16:52 +0200730 if (!ca_cert_nss.empty()) {
Paul Stewart5baebb72013-03-14 11:43:29 -0700731 DCHECK(ca_cert.empty() && ca_cert_pem.empty());
Darin Petkovb451d6e2012-04-23 11:56:41 +0200732 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200733 vector<char> id(vpnhost.begin(), vpnhost.end());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200734 FilePath certfile = nss_->GetPEMCertfile(ca_cert_nss, id);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200735 if (certfile.empty()) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200736 Error::PopulateAndLog(
737 error,
738 Error::kInvalidArguments,
739 "Unable to extract NSS CA certificate: " + ca_cert_nss);
740 return false;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200741 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700742 AppendOption("ca", certfile.value(), options);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200743 return true;
Paul Stewart5baebb72013-03-14 11:43:29 -0700744 } else if (!ca_cert_pem.empty()) {
745 DCHECK(ca_cert.empty() && ca_cert_nss.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 Stewart5baebb72013-03-14 11:43:29 -0700757 DCHECK(!ca_cert.empty() && ca_cert_nss.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 Stewart406c4732013-08-01 09:30:12 -0700762void OpenVPNDriver::InitPKCS11Options(vector<vector<string>> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200763 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200764 if (!id.empty()) {
765 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200766 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200767 if (provider.empty()) {
768 provider = kDefaultPKCS11Provider;
769 }
Paul Stewartb26347a2013-08-02 12:12:09 -0700770 AppendOption("pkcs11-providers", provider, options);
771 AppendOption("pkcs11-id", id, options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200772 }
773}
774
Paul Stewart406c4732013-08-01 09:30:12 -0700775void OpenVPNDriver::InitClientAuthOptions(vector<vector<string>> *options) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700776 bool has_cert = AppendValueOption(kOpenVPNCertProperty, "cert", options) ||
Paul Stewartbb985102013-06-27 16:51:11 -0700777 !args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "").empty();
Paul Stewartb26347a2013-08-02 12:12:09 -0700778 bool has_key = AppendValueOption(kOpenVPNKeyProperty, "key", options);
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200779 // If the AuthUserPass property is set, or the User property is non-empty, or
780 // there's neither a key, nor a cert available, specify user-password client
781 // authentication.
782 if (args()->ContainsString(flimflam::kOpenVPNAuthUserPassProperty) ||
783 !args()->LookupString(flimflam::kOpenVPNUserProperty, "").empty() ||
784 (!has_cert && !has_key)) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700785 AppendOption("auth-user-pass", options);
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200786 }
787}
788
Darin Petkove0d5dd12012-04-04 16:10:48 +0200789bool OpenVPNDriver::InitManagementChannelOptions(
Paul Stewart406c4732013-08-01 09:30:12 -0700790 vector<vector<string>> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200791 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200792 Error::PopulateAndLog(
793 error, Error::kInternalError, "Unable to setup management channel.");
794 return false;
795 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200796 // If there's a connected default service already, allow the openvpn client to
797 // establish connection as soon as it's started. Otherwise, hold the client
798 // until an underlying service connects and OnDefaultServiceChanged is
799 // invoked.
Darin Petkov4cbff5b2013-01-29 16:29:05 +0100800 if (manager()->IsOnline()) {
Darin Petkova5e07ef2012-07-09 14:27:57 +0200801 management_server_->ReleaseHold();
802 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200803 return true;
804}
805
Paul Stewart406c4732013-08-01 09:30:12 -0700806void OpenVPNDriver::InitLoggingOptions(vector<vector<string>> *options) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700807 AppendOption("syslog", options);
Darin Petkov55771b72012-04-25 09:25:19 +0200808
809 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
810 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
811 verb = "3";
812 }
813 if (!verb.empty()) {
Paul Stewartb26347a2013-08-02 12:12:09 -0700814 AppendOption("verb", verb, options);
Darin Petkov55771b72012-04-25 09:25:19 +0200815 }
816}
817
Paul Stewart406c4732013-08-01 09:30:12 -0700818void OpenVPNDriver::AppendOption(
819 const string &option, vector<vector<string>> *options) {
820 options->push_back(vector<string>{ option });
821}
822
823void OpenVPNDriver::AppendOption(
824 const string &option,
825 const string &value,
826 vector<vector<string>> *options) {
827 options->push_back(vector<string>{ option, value });
828}
829
830void OpenVPNDriver::AppendOption(
831 const string &option,
832 const string &value0,
833 const string &value1,
834 vector<vector<string>> *options) {
835 options->push_back(vector<string>{ option, value0, value1 });
836}
837
Darin Petkov46463022012-03-29 14:57:32 +0200838bool OpenVPNDriver::AppendValueOption(
Paul Stewart406c4732013-08-01 09:30:12 -0700839 const string &property,
840 const string &option,
841 vector<vector<string>> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200842 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100843 if (!value.empty()) {
Paul Stewart406c4732013-08-01 09:30:12 -0700844 AppendOption(option, value, options);
Darin Petkov46463022012-03-29 14:57:32 +0200845 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100846 }
Darin Petkov46463022012-03-29 14:57:32 +0200847 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100848}
849
Darin Petkov46463022012-03-29 14:57:32 +0200850bool OpenVPNDriver::AppendFlag(
Paul Stewart406c4732013-08-01 09:30:12 -0700851 const string &property,
852 const string &option,
853 vector<vector<string>> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200854 if (args()->ContainsString(property)) {
Paul Stewart406c4732013-08-01 09:30:12 -0700855 AppendOption(option, options);
Darin Petkov46463022012-03-29 14:57:32 +0200856 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100857 }
Darin Petkov46463022012-03-29 14:57:32 +0200858 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100859}
860
Darin Petkov6aa21872012-03-09 16:10:19 +0100861void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700862 SLOG(VPN, 2) << __func__;
Darin Petkovaba89322013-03-11 14:48:22 +0100863 IdleService();
Darin Petkov6aa21872012-03-09 16:10:19 +0100864}
865
Darin Petkov5eb05422012-05-11 15:45:25 +0200866void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkova42afe32013-02-05 16:53:52 +0100867 LOG(INFO) << "Underlying connection disconnected.";
868 // Restart the OpenVPN client forcing a reconnect attempt.
869 management_server_->Restart();
870 // Indicate reconnect state right away to drop the VPN connection and start
871 // the connect timeout. This ensures that any miscommunication between shill
872 // and openvpn will not lead to a permanently stale connectivity state. Note
873 // that a subsequent invocation of OnReconnecting due to a RECONNECTING
874 // message will essentially be a no-op.
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100875 OnReconnecting(kReconnectReasonOffline);
Darin Petkova42afe32013-02-05 16:53:52 +0100876}
877
878void OpenVPNDriver::OnConnectTimeout() {
879 VPNDriver::OnConnectTimeout();
Darin Petkov1c049c72013-03-21 13:15:45 +0100880 Service::ConnectFailure failure =
881 management_server_->state() == OpenVPNManagementServer::kStateResolve ?
882 Service::kFailureDNSLookup : Service::kFailureConnect;
883 FailService(failure, Service::kErrorDetailsNone);
Darin Petkov5eb05422012-05-11 15:45:25 +0200884}
885
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100886void OpenVPNDriver::OnReconnecting(ReconnectReason reason) {
887 LOG(INFO) << __func__ << "(" << reason << ")";
888 int timeout_seconds = GetReconnectTimeoutSeconds(reason);
889 if (reason == kReconnectReasonTLSError &&
890 timeout_seconds < connect_timeout_seconds()) {
891 // Reconnect due to TLS error happens during connect so we need to cancel
892 // the original connect timeout first and then reduce the time limit.
893 StopConnectTimeout();
894 }
895 StartConnectTimeout(timeout_seconds);
Darin Petkova5e07ef2012-07-09 14:27:57 +0200896 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
897 // might be in hold state if the VPN connection was previously established
898 // successfully. The hold will be released by OnDefaultServiceChanged when a
899 // new default service connects. This ensures that the client will use a fully
900 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200901 if (device_) {
mukesh agrawal9da07772013-05-15 14:15:17 -0700902 device_->DropConnection();
Darin Petkov271fe522012-03-27 13:47:29 +0200903 }
904 if (service_) {
905 service_->SetState(Service::kStateAssociating);
906 }
907}
908
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100909// static
910int OpenVPNDriver::GetReconnectTimeoutSeconds(ReconnectReason reason) {
911 switch (reason) {
912 case kReconnectReasonOffline:
913 return kReconnectOfflineTimeoutSeconds;
914 case kReconnectReasonTLSError:
915 return kReconnectTLSErrorTimeoutSeconds;
916 default:
917 break;
918 }
919 return kDefaultConnectTimeoutSeconds;
920}
921
Paul Stewart39964fa2012-04-04 09:50:25 -0700922string OpenVPNDriver::GetProviderType() const {
923 return flimflam::kProviderOpenVpn;
924}
925
Darin Petkovb536a742012-04-26 11:31:28 +0200926KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
927 SLOG(VPN, 2) << __func__;
928 KeyValueStore props = VPNDriver::GetProvider(error);
929 props.SetBool(flimflam::kPassphraseRequiredProperty,
930 args()->LookupString(
931 flimflam::kOpenVPNPasswordProperty, "").empty());
932 return props;
933}
934
Darin Petkov1a462de2012-05-02 11:10:48 +0200935// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
936// singleton if it's used outside OpenVPN.
937bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
938 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
939 string contents;
940 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
941 LOG(ERROR) << "Unable to read the lsb-release file: "
942 << lsb_release_file_.value();
943 return false;
944 }
945 vector<string> lines;
946 SplitString(contents, '\n', &lines);
Paul Stewart406c4732013-08-01 09:30:12 -0700947 for (const auto &line : lines) {
948 size_t assign = line.find('=');
Darin Petkov1a462de2012-05-02 11:10:48 +0200949 if (assign == string::npos) {
950 continue;
951 }
Paul Stewart406c4732013-08-01 09:30:12 -0700952 (*lsb_release)[line.substr(0, assign)] = line.substr(assign + 1);
Darin Petkov1a462de2012-05-02 11:10:48 +0200953 }
954 return true;
955}
956
957void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
958 // Adds the platform name and version to the environment so that openvpn can
959 // send them to the server when OpenVPN.PushPeerInfo is set.
960 map<string, string> lsb_release;
961 ParseLSBRelease(&lsb_release);
962 string platform_name = lsb_release[kChromeOSReleaseName];
963 if (!platform_name.empty()) {
964 environment->push_back("IV_PLAT=" + platform_name);
965 }
966 string platform_version = lsb_release[kChromeOSReleaseVersion];
967 if (!platform_version.empty()) {
968 environment->push_back("IV_PLAT_REL=" + platform_version);
969 }
970}
971
Darin Petkova5e07ef2012-07-09 14:27:57 +0200972void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
973 SLOG(VPN, 2) << __func__
Darin Petkov457728b2013-01-09 09:49:08 +0100974 << "(" << (service ? service->unique_name() : "-") << ")";
Darin Petkova5e07ef2012-07-09 14:27:57 +0200975 // Allow the openvpn client to connect/reconnect only over a connected
976 // underlying default service. If there's no default connected service, hold
977 // the openvpn client until an underlying connection is established. If the
978 // default service is our VPN service, hold the openvpn client on reconnect so
979 // that the VPN connection can be torn down fully before a new connection
980 // attempt is made over the underlying service.
981 if (service && service != service_ && service->IsConnected()) {
982 management_server_->ReleaseHold();
983 } else {
984 management_server_->Hold();
985 }
986}
987
Paul Stewart91a43cb2013-03-02 21:34:15 -0800988void OpenVPNDriver::ReportConnectionMetrics() {
989 metrics_->SendEnumToUMA(
990 Metrics::kMetricVpnDriver,
991 Metrics::kVpnDriverOpenVpn,
992 Metrics::kMetricVpnDriverMax);
993
Paul Stewarte8e71da2013-03-20 08:48:33 -0700994 if (args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "") != "" ||
995 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -0800996 metrics_->SendEnumToUMA(
997 Metrics::kMetricVpnRemoteAuthenticationType,
998 Metrics::kVpnRemoteAuthenticationTypeOpenVpnCertificate,
999 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1000 } else {
1001 metrics_->SendEnumToUMA(
1002 Metrics::kMetricVpnRemoteAuthenticationType,
1003 Metrics::kVpnRemoteAuthenticationTypeOpenVpnDefault,
1004 Metrics::kMetricVpnRemoteAuthenticationTypeMax);
1005 }
1006
1007 bool has_user_authentication = false;
Paul Stewarte8e71da2013-03-20 08:48:33 -07001008 if (args()->LookupString(flimflam::kOpenVPNOTPProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001009 metrics_->SendEnumToUMA(
1010 Metrics::kMetricVpnUserAuthenticationType,
1011 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePasswordOtp,
1012 Metrics::kMetricVpnUserAuthenticationTypeMax);
1013 has_user_authentication = true;
1014 }
Paul Stewarte8e71da2013-03-20 08:48:33 -07001015 if (args()->LookupString(flimflam::kOpenVPNAuthUserPassProperty, "") != ""||
1016 args()->LookupString(flimflam::kOpenVPNUserProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001017 metrics_->SendEnumToUMA(
1018 Metrics::kMetricVpnUserAuthenticationType,
1019 Metrics::kVpnUserAuthenticationTypeOpenVpnUsernamePassword,
1020 Metrics::kMetricVpnUserAuthenticationTypeMax);
1021 has_user_authentication = true;
1022 }
Paul Stewarte8e71da2013-03-20 08:48:33 -07001023 if (args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "") != "" ||
1024 args()->LookupString(kOpenVPNCertProperty, "") != "") {
Paul Stewart91a43cb2013-03-02 21:34:15 -08001025 metrics_->SendEnumToUMA(
1026 Metrics::kMetricVpnUserAuthenticationType,
1027 Metrics::kVpnUserAuthenticationTypeOpenVpnCertificate,
1028 Metrics::kMetricVpnUserAuthenticationTypeMax);
1029 has_user_authentication = true;
1030 }
1031 if (!has_user_authentication) {
1032 metrics_->SendEnumToUMA(
1033 Metrics::kMetricVpnUserAuthenticationType,
1034 Metrics::kVpnUserAuthenticationTypeOpenVpnNone,
1035 Metrics::kMetricVpnUserAuthenticationTypeMax);
1036 }
1037}
1038
Darin Petkov33af05c2012-02-28 10:10:30 +01001039} // namespace shill