blob: 048401bd7a2d5947a2302f89023e1b4c9c2876dd [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 Stewartce4ec192012-03-14 12:53:46 -070015#include "shill/connection.h"
Paul Stewartca6abd42012-03-01 15:45:29 -080016#include "shill/device_info.h"
Darin Petkov14c29ec2012-03-02 11:34:19 +010017#include "shill/dhcp_config.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010018#include "shill/error.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070019#include "shill/logging.h"
Paul Stewartce4ec192012-03-14 12:53:46 -070020#include "shill/manager.h"
Darin Petkov3c5e4dc2012-04-02 14:44:27 +020021#include "shill/nss.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"
Darin Petkovf20994f2012-03-05 16:12:19 +010026#include "shill/vpn.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;
Darin Petkov78f63262012-03-26 01:30:24 +020030using base::SplitString;
Darin Petkova5e07ef2012-07-09 14:27:57 +020031using base::Unretained;
Darin Petkov5a850472012-06-06 15:44:24 +020032using base::WeakPtr;
Darin Petkov14c29ec2012-03-02 11:34:19 +010033using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010034using std::string;
35using std::vector;
36
Darin Petkov33af05c2012-02-28 10:10:30 +010037namespace shill {
38
Darin Petkova9b1fed2012-02-29 11:49:05 +010039namespace {
Darin Petkov5a850472012-06-06 15:44:24 +020040
Darin Petkov14c29ec2012-03-02 11:34:19 +010041const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
42const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
43const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
44const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
45const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
46const char kOpenVPNRouteOptionPrefix[] = "route_";
47const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
48const char kOpenVPNTrustedIP[] = "trusted_ip";
49const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010050
Darin Petkove0d5dd12012-04-04 16:10:48 +020051const char kDefaultPKCS11Provider[] = "libchaps.so";
52
Darin Petkovf3c71d72012-03-21 12:32:15 +010053// TODO(petkov): Move to chromeos/dbus/service_constants.h.
Darin Petkovf3c71d72012-03-21 12:32:15 +010054const char kOpenVPNPingProperty[] = "OpenVPN.Ping";
55const char kOpenVPNPingExitProperty[] = "OpenVPN.PingExit";
56const char kOpenVPNPingRestartProperty[] = "OpenVPN.PingRestart";
57const char kOpenVPNTLSAuthProperty[] = "OpenVPN.TLSAuth";
58const char kOpenVPNVerbProperty[] = "OpenVPN.Verb";
59const char kVPNMTUProperty[] = "VPN.MTU";
Darin Petkov5a850472012-06-06 15:44:24 +020060
Paul Stewartebd38562012-03-23 13:06:40 -070061} // namespace
62
Darin Petkov4e1b3f82012-09-27 13:22:37 +020063// TODO(petkov): Move to chromeos/dbus/service_constants.h.
64const char OpenVPNDriver::kOpenVPNCertProperty[] = "OpenVPN.Cert";
65const char OpenVPNDriver::kOpenVPNKeyProperty[] = "OpenVPN.Key";
Paul Stewartebd38562012-03-23 13:06:40 -070066// static
Darin Petkovc418b4b2012-10-05 11:42:52 +020067const char OpenVPNDriver::kDefaultCACertificates[] =
68 "/etc/ssl/certs/ca-certificates.crt";
Darin Petkovca8a0e62012-09-26 13:16:52 +020069// static
Paul Stewartebd38562012-03-23 13:06:40 -070070const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
71// static
Darin Petkov728faa92012-10-12 11:25:47 +020072const char OpenVPNDriver::kOpenVPNScript[] = SHIMDIR "/openvpn-script";
Paul Stewartebd38562012-03-23 13:06:40 -070073// static
Darin Petkovd4325392012-04-23 15:48:22 +020074const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020075 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
76 { flimflam::kOpenVPNAuthProperty, 0 },
77 { flimflam::kOpenVPNAuthRetryProperty, 0 },
78 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
79 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
80 { flimflam::kOpenVPNCaCertProperty, 0 },
81 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020082 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020083 { flimflam::kOpenVPNCompLZOProperty, 0 },
84 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
85 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
86 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020087 { flimflam::kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020088 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
89 { flimflam::kOpenVPNPasswordProperty,
90 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020091 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020092 { flimflam::kOpenVPNPortProperty, 0 },
93 { flimflam::kOpenVPNProtoProperty, 0 },
94 { flimflam::kOpenVPNProviderProperty, 0 },
95 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
96 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
97 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
98 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
99 { flimflam::kOpenVPNRenegSecProperty, 0 },
100 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
101 { flimflam::kOpenVPNShaperProperty, 0 },
102 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
103 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
104 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
105 { flimflam::kOpenVPNUserProperty, 0 },
106 { flimflam::kProviderHostProperty, 0 },
107 { flimflam::kProviderNameProperty, 0 },
108 { flimflam::kProviderTypeProperty, 0 },
109 { kOpenVPNCertProperty, 0 },
110 { kOpenVPNKeyProperty, 0 },
111 { kOpenVPNPingExitProperty, 0 },
112 { kOpenVPNPingProperty, 0 },
113 { kOpenVPNPingRestartProperty, 0 },
114 { kOpenVPNTLSAuthProperty, 0 },
115 { kOpenVPNVerbProperty, 0 },
116 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700117
118 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200119 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100120};
Paul Stewart291a4732012-03-14 19:19:02 -0700121
Darin Petkov1a462de2012-05-02 11:10:48 +0200122// static
123const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
124// static
125const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
126//static
127const char OpenVPNDriver::kChromeOSReleaseVersion[] =
128 "CHROMEOS_RELEASE_VERSION";
129
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()),
Darin Petkov5a850472012-06-06 15:44:24 +0200143 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200144 lsb_release_file_(kLSBReleaseFile),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100145 pid_(0),
Darin Petkova5e07ef2012-07-09 14:27:57 +0200146 child_watch_tag_(0),
147 default_service_callback_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100148
Darin Petkov36a3ace2012-03-06 17:22:14 +0100149OpenVPNDriver::~OpenVPNDriver() {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100150 Cleanup(Service::kStateIdle);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100151}
152
Darin Petkov3f9131c2012-03-20 11:37:32 +0100153void OpenVPNDriver::Cleanup(Service::ConnectState state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700154 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
155 << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200156 StopConnectTimeout();
Darin Petkov46463022012-03-29 14:57:32 +0200157 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200158 if (!tls_auth_file_.empty()) {
159 file_util::Delete(tls_auth_file_, false);
160 tls_auth_file_.clear();
161 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200162 if (default_service_callback_tag_) {
163 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
164 default_service_callback_tag_ = 0;
165 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100166 if (child_watch_tag_) {
167 glib_->SourceRemove(child_watch_tag_);
168 child_watch_tag_ = 0;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100169 }
170 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200171 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100172 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200173 interface_index = device_->interface_index();
Darin Petkov029d3532012-04-18 14:38:04 +0200174 device_->OnDisconnected();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500175 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100176 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200177 }
178 if (pid_) {
179 Closure callback;
180 if (interface_index >= 0) {
181 callback =
182 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
183 interface_index = -1;
184 }
185 process_killer_->Kill(pid_, callback);
186 pid_ = 0;
187 }
188 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100189 device_info_->DeleteInterface(interface_index);
190 }
191 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100192 if (service_) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100193 service_->SetState(state);
Darin Petkov79d74c92012-03-07 17:20:32 +0100194 service_ = NULL;
195 }
Darin Petkov3189a472012-10-05 09:55:33 +0200196 ip_properties_ = IPConfig::Properties();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100197}
198
199bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700200 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100201
202 vector<string> options;
203 Error error;
204 InitOptions(&options, &error);
205 if (error.IsFailure()) {
206 return false;
207 }
Darin Petkov728faa92012-10-12 11:25:47 +0200208 LOG(INFO) << "OpenVPN process options: " << JoinString(options, ' ');
Darin Petkov36a3ace2012-03-06 17:22:14 +0100209
210 // TODO(petkov): This code needs to be abstracted away in a separate external
211 // process module (crosbug.com/27131).
212 vector<char *> process_args;
213 process_args.push_back(const_cast<char *>(kOpenVPNPath));
214 for (vector<string>::const_iterator it = options.begin();
215 it != options.end(); ++it) {
216 process_args.push_back(const_cast<char *>(it->c_str()));
217 }
218 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200219
220 vector<string> environment;
221 InitEnvironment(&environment);
222
223 vector<char *> process_env;
224 for (vector<string>::const_iterator it = environment.begin();
225 it != environment.end(); ++it) {
226 process_env.push_back(const_cast<char *>(it->c_str()));
227 }
228 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100229
230 CHECK(!pid_);
231 // Redirect all openvpn output to stderr.
232 int stderr_fd = fileno(stderr);
233 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
Darin Petkov1a462de2012-05-02 11:10:48 +0200234 process_env.data(),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100235 G_SPAWN_DO_NOT_REAP_CHILD,
236 NULL,
237 NULL,
238 &pid_,
239 NULL,
240 &stderr_fd,
241 &stderr_fd,
242 NULL)) {
243 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
244 return false;
245 }
246 CHECK(!child_watch_tag_);
247 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
248 return true;
249}
250
251// static
252void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700253 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100254 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
255 me->child_watch_tag_ = 0;
256 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200257 me->pid_ = 0;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100258 me->Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100259 // TODO(petkov): Figure if we need to restart the connection.
260}
Darin Petkov33af05c2012-02-28 10:10:30 +0100261
Darin Petkov5a850472012-06-06 15:44:24 +0200262// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200263void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200264 int interface_index) {
265 if (device_info) {
266 LOG(INFO) << "Deleting interface " << interface_index;
267 device_info->DeleteInterface(interface_index);
268 }
269}
270
Paul Stewartca6abd42012-03-01 15:45:29 -0800271bool OpenVPNDriver::ClaimInterface(const string &link_name,
272 int interface_index) {
273 if (link_name != tunnel_interface_) {
274 return false;
275 }
276
Ben Chanfad4a0b2012-04-18 15:49:59 -0700277 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800278
Darin Petkovf20994f2012-03-05 16:12:19 +0100279 CHECK(!device_);
Darin Petkov602303f2012-06-06 12:15:59 +0200280 device_ = new VPN(control_, dispatcher(), metrics_, manager(),
Darin Petkovf20994f2012-03-05 16:12:19 +0100281 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500282 device_->SetEnabled(true);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500283
Darin Petkov36a3ace2012-03-06 17:22:14 +0100284 rpc_task_.reset(new RPCTask(control_, this));
285 if (!SpawnOpenVPN()) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100286 Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100287 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200288 default_service_callback_tag_ =
289 manager()->RegisterDefaultServiceCallback(
290 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Paul Stewartca6abd42012-03-01 15:45:29 -0800291 return true;
292}
293
Darin Petkov209e6292012-04-20 11:33:32 +0200294void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
295 NOTREACHED();
296}
297
Darin Petkov36a3ace2012-03-06 17:22:14 +0100298void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100299 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200300 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100301 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100302 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100303 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100304 }
Darin Petkov3189a472012-10-05 09:55:33 +0200305 // On restart/reconnect, update the existing IP configuration.
306 ParseIPConfiguration(dict, &ip_properties_);
307 device_->SelectService(service_);
308 device_->UpdateIPConfig(ip_properties_);
Darin Petkov602303f2012-06-06 12:15:59 +0200309 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100310}
311
312// static
313void OpenVPNDriver::ParseIPConfiguration(
314 const map<string, string> &configuration,
315 IPConfig::Properties *properties) {
316 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100317 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100318 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200319 if (!properties->subnet_prefix) {
320 properties->subnet_prefix =
321 IPAddress::GetMaxPrefixLength(properties->address_family);
322 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100323 for (map<string, string>::const_iterator it = configuration.begin();
324 it != configuration.end(); ++it) {
325 const string &key = it->first;
326 const string &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700327 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100328 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
329 properties->address = value;
330 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
331 properties->broadcast_address = value;
332 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700333 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100334 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
335 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
336 properties->peer_address = value;
337 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
338 properties->gateway = value;
339 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700340 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100341 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
342 int mtu = 0;
343 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
344 properties->mtu = mtu;
345 } else {
346 LOG(ERROR) << "MTU " << value << " ignored.";
347 }
348 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
349 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
350 int order = 0;
351 if (base::StringToInt(suffix, &order)) {
352 foreign_options[order] = value;
353 } else {
354 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
355 }
356 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100357 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
358 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100359 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700360 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100361 }
362 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100363 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100364 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100365}
366
367// static
368void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
369 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200370 vector<string> domain_search;
371 vector<string> dns_servers;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100372 for (ForeignOptions::const_iterator it = options.begin();
373 it != options.end(); ++it) {
Darin Petkove8587e32012-07-02 13:56:07 +0200374 ParseForeignOption(it->second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100375 }
Darin Petkove8587e32012-07-02 13:56:07 +0200376 if (!domain_search.empty()) {
377 properties->domain_search.swap(domain_search);
378 }
379 LOG_IF(WARNING, properties->domain_search.empty())
380 << "No search domains provided.";
381 if (!dns_servers.empty()) {
382 properties->dns_servers.swap(dns_servers);
383 }
384 LOG_IF(WARNING, properties->dns_servers.empty())
385 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100386}
387
388// static
389void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200390 vector<string> *domain_search,
391 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700392 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100393 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200394 SplitString(option, ' ', &tokens);
395 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100396 return;
397 }
398 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200399 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100400 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200401 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100402 }
403}
404
Darin Petkov60596742012-03-05 12:17:17 +0100405// static
406IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
407 const string &prefix, const string &key, RouteOptions *routes) {
408 int order = 0;
409 if (!StartsWithASCII(key, prefix, false) ||
410 !base::StringToInt(key.substr(prefix.size()), &order)) {
411 return NULL;
412 }
413 return &(*routes)[order];
414}
415
416// static
417void OpenVPNDriver::ParseRouteOption(
418 const string &key, const string &value, RouteOptions *routes) {
419 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
420 if (route) {
421 route->host = value;
422 return;
423 }
424 route = GetRouteOptionEntry("netmask_", key, routes);
425 if (route) {
426 route->netmask = value;
427 return;
428 }
429 route = GetRouteOptionEntry("gateway_", key, routes);
430 if (route) {
431 route->gateway = value;
432 return;
433 }
434 LOG(WARNING) << "Unknown route option ignored: " << key;
435}
436
437// static
438void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
439 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200440 vector<IPConfig::Route> new_routes;
Darin Petkov60596742012-03-05 12:17:17 +0100441 for (RouteOptions::const_iterator it = routes.begin();
442 it != routes.end(); ++it) {
443 const IPConfig::Route &route = it->second;
444 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
445 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
446 continue;
447 }
Darin Petkove8587e32012-07-02 13:56:07 +0200448 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100449 }
Darin Petkove8587e32012-07-02 13:56:07 +0200450 if (!new_routes.empty()) {
451 properties->routes.swap(new_routes);
452 }
453 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100454}
455
Darin Petkov4b944842012-09-21 10:48:48 +0200456// static
457bool OpenVPNDriver::SplitPortFromHost(
458 const string &host, string *name, string *port) {
459 vector<string> tokens;
460 SplitString(host, ':', &tokens);
461 int port_number = 0;
462 if (tokens.size() != 2 || tokens[0].empty() || tokens[1].empty() ||
463 !IsAsciiDigit(tokens[1][0]) ||
464 !base::StringToInt(tokens[1], &port_number) || port_number > kuint16max) {
465 return false;
466 }
467 *name = tokens[0];
468 *port = tokens[1];
469 return true;
470}
471
Darin Petkov602303f2012-06-06 12:15:59 +0200472void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
473 StartConnectTimeout();
Darin Petkov79d74c92012-03-07 17:20:32 +0100474 service_ = service;
475 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100476 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
477 Error::PopulateAndLog(
478 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov3f9131c2012-03-20 11:37:32 +0100479 Cleanup(Service::kStateFailure);
Darin Petkovf20994f2012-03-05 16:12:19 +0100480 }
481 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100482}
483
Darin Petkovfe6a9372012-02-28 16:25:06 +0100484void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200485 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100486 if (vpnhost.empty()) {
487 Error::PopulateAndLog(
488 error, Error::kInvalidArguments, "VPN host not specified.");
489 return;
490 }
491 options->push_back("--client");
492 options->push_back("--tls-client");
Darin Petkov4b944842012-09-21 10:48:48 +0200493
Darin Petkovfe6a9372012-02-28 16:25:06 +0100494 options->push_back("--remote");
Darin Petkov4b944842012-09-21 10:48:48 +0200495 string host_name, host_port;
496 if (SplitPortFromHost(vpnhost, &host_name, &host_port)) {
497 DCHECK(!host_name.empty());
498 DCHECK(!host_port.empty());
499 options->push_back(host_name);
500 options->push_back(host_port);
501 } else {
502 options->push_back(vpnhost);
503 }
504
Darin Petkovfe6a9372012-02-28 16:25:06 +0100505 options->push_back("--nobind");
506 options->push_back("--persist-key");
507 options->push_back("--persist-tun");
508
Darin Petkovf20994f2012-03-05 16:12:19 +0100509 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800510 options->push_back("--dev");
511 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100512 options->push_back("--dev-type");
513 options->push_back("tun");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100514
Darin Petkov55771b72012-04-25 09:25:19 +0200515 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100516
Darin Petkovf3c71d72012-03-21 12:32:15 +0100517 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100518 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
519 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100520 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200521 {
522 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200523 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200524 if (!contents.empty()) {
525 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
526 file_util::WriteFile(
527 tls_auth_file_, contents.data(), contents.size()) !=
528 static_cast<int>(contents.size())) {
529 Error::PopulateAndLog(
530 error, Error::kInternalError, "Unable to setup tls-auth file.");
531 return;
532 }
533 options->push_back("--tls-auth");
534 options->push_back(tls_auth_file_.value());
535 }
536 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100537 AppendValueOption(
538 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
539 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
540 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
541 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
542 AppendValueOption(
543 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
544 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
545 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
546 AppendFlag(
547 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
548 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
549 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
550 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
551 "--server-poll-timeout", options);
552
Darin Petkovca8a0e62012-09-26 13:16:52 +0200553 if (!InitCAOptions(options, error)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200554 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200555 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100556
557 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100558 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
559 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
560 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100561
Darin Petkovfe6a9372012-02-28 16:25:06 +0100562 AppendValueOption(
563 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100564
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200565 InitClientAuthOptions(options);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200566 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100567
568 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100569 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200570 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100571 if (remote_cert_tls.empty()) {
572 remote_cert_tls = "server";
573 }
574 if (remote_cert_tls != "none") {
575 options->push_back("--remote-cert-tls");
576 options->push_back(remote_cert_tls);
577 }
578
579 // This is an undocumented command line argument that works like a .cfg file
580 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
581 AppendValueOption(
582 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
583 // TODO(sleffler): Support more than one eku parameter.
584 AppendValueOption(
585 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
586 AppendValueOption(
587 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
588
Darin Petkove0d5dd12012-04-04 16:10:48 +0200589 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200590 return;
591 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100592
Darin Petkova9b1fed2012-02-29 11:49:05 +0100593 // Setup openvpn-script options and RPC information required to send back
594 // Layer 3 configuration.
595 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200596 options->push_back(kRPCTaskServiceVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100597 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
598 options->push_back("--setenv");
Darin Petkov728faa92012-10-12 11:25:47 +0200599 options->push_back(kRPCTaskPathVariable);
Darin Petkova9b1fed2012-02-29 11:49:05 +0100600 options->push_back(rpc_task_->GetRpcIdentifier());
601 options->push_back("--script-security");
602 options->push_back("2");
603 options->push_back("--up");
604 options->push_back(kOpenVPNScript);
605 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100606
607 // Disable openvpn handling since we do route+ifconfig work.
608 options->push_back("--route-noexec");
609 options->push_back("--ifconfig-noexec");
610
611 // Drop root privileges on connection and enable callback scripts to send
612 // notify messages.
613 options->push_back("--user");
614 options->push_back("openvpn");
615 options->push_back("--group");
616 options->push_back("openvpn");
617}
618
Darin Petkovca8a0e62012-09-26 13:16:52 +0200619bool OpenVPNDriver::InitCAOptions(vector<string> *options, Error *error) {
Darin Petkovc418b4b2012-10-05 11:42:52 +0200620 options->push_back("--ca");
Darin Petkovb451d6e2012-04-23 11:56:41 +0200621 string ca_cert =
Darin Petkovca8a0e62012-09-26 13:16:52 +0200622 args()->LookupString(flimflam::kOpenVPNCaCertProperty, "");
623 string ca_cert_nss =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200624 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Darin Petkovca8a0e62012-09-26 13:16:52 +0200625 if (ca_cert.empty() && ca_cert_nss.empty()) {
626 // Use default CAs if no CA certificate is provided.
Darin Petkovc418b4b2012-10-05 11:42:52 +0200627 options->push_back(kDefaultCACertificates);
Darin Petkovca8a0e62012-09-26 13:16:52 +0200628 return true;
629 }
630 if (!ca_cert.empty() && !ca_cert_nss.empty()) {
631 Error::PopulateAndLog(error,
632 Error::kInvalidArguments,
633 "Can't specify both CACert and CACertNSS.");
634 return false;
635 }
Darin Petkovca8a0e62012-09-26 13:16:52 +0200636 if (!ca_cert_nss.empty()) {
637 DCHECK(ca_cert.empty());
Darin Petkovb451d6e2012-04-23 11:56:41 +0200638 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200639 vector<char> id(vpnhost.begin(), vpnhost.end());
Darin Petkovca8a0e62012-09-26 13:16:52 +0200640 FilePath certfile = nss_->GetPEMCertfile(ca_cert_nss, id);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200641 if (certfile.empty()) {
Darin Petkovca8a0e62012-09-26 13:16:52 +0200642 Error::PopulateAndLog(
643 error,
644 Error::kInvalidArguments,
645 "Unable to extract NSS CA certificate: " + ca_cert_nss);
646 return false;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200647 }
Darin Petkovca8a0e62012-09-26 13:16:52 +0200648 options->push_back(certfile.value());
649 return true;
Darin Petkove0d5dd12012-04-04 16:10:48 +0200650 }
Darin Petkovca8a0e62012-09-26 13:16:52 +0200651 DCHECK(!ca_cert.empty() && ca_cert_nss.empty());
652 options->push_back(ca_cert);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200653 return true;
654}
655
656void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200657 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200658 if (!id.empty()) {
659 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200660 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200661 if (provider.empty()) {
662 provider = kDefaultPKCS11Provider;
663 }
664 options->push_back("--pkcs11-providers");
665 options->push_back(provider);
666 options->push_back("--pkcs11-id");
667 options->push_back(id);
668 }
669}
670
Darin Petkov4e1b3f82012-09-27 13:22:37 +0200671void OpenVPNDriver::InitClientAuthOptions(vector<string> *options) {
672 bool has_cert = AppendValueOption(kOpenVPNCertProperty, "--cert", options);
673 bool has_key = AppendValueOption(kOpenVPNKeyProperty, "--key", options);
674 // If the AuthUserPass property is set, or the User property is non-empty, or
675 // there's neither a key, nor a cert available, specify user-password client
676 // authentication.
677 if (args()->ContainsString(flimflam::kOpenVPNAuthUserPassProperty) ||
678 !args()->LookupString(flimflam::kOpenVPNUserProperty, "").empty() ||
679 (!has_cert && !has_key)) {
680 options->push_back("--auth-user-pass");
681 }
682}
683
Darin Petkove0d5dd12012-04-04 16:10:48 +0200684bool OpenVPNDriver::InitManagementChannelOptions(
685 vector<string> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200686 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200687 Error::PopulateAndLog(
688 error, Error::kInternalError, "Unable to setup management channel.");
689 return false;
690 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200691 // If there's a connected default service already, allow the openvpn client to
692 // establish connection as soon as it's started. Otherwise, hold the client
693 // until an underlying service connects and OnDefaultServiceChanged is
694 // invoked.
695 ServiceRefPtr service = manager()->GetDefaultService();
696 if (service && service->IsConnected()) {
697 management_server_->ReleaseHold();
698 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200699 return true;
700}
701
Darin Petkov55771b72012-04-25 09:25:19 +0200702void OpenVPNDriver::InitLoggingOptions(vector<string> *options) {
703 options->push_back("--syslog");
704
705 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
706 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
707 verb = "3";
708 }
709 if (!verb.empty()) {
710 options->push_back("--verb");
711 options->push_back(verb);
712 }
713}
714
Darin Petkov46463022012-03-29 14:57:32 +0200715bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100716 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200717 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100718 if (!value.empty()) {
719 options->push_back(option);
720 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200721 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100722 }
Darin Petkov46463022012-03-29 14:57:32 +0200723 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100724}
725
Darin Petkov46463022012-03-29 14:57:32 +0200726bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100727 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200728 if (args()->ContainsString(property)) {
Darin Petkovfe6a9372012-02-28 16:25:06 +0100729 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200730 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100731 }
Darin Petkov46463022012-03-29 14:57:32 +0200732 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100733}
734
Darin Petkov6aa21872012-03-09 16:10:19 +0100735void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700736 SLOG(VPN, 2) << __func__;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100737 Cleanup(Service::kStateIdle);
Darin Petkov6aa21872012-03-09 16:10:19 +0100738}
739
Darin Petkov5eb05422012-05-11 15:45:25 +0200740void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkov602303f2012-06-06 12:15:59 +0200741 LOG(ERROR) << "VPN connection disconnected.";
Darin Petkov5eb05422012-05-11 15:45:25 +0200742 Cleanup(Service::kStateFailure);
743}
744
Darin Petkov271fe522012-03-27 13:47:29 +0200745void OpenVPNDriver::OnReconnecting() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700746 SLOG(VPN, 2) << __func__;
Darin Petkov602303f2012-06-06 12:15:59 +0200747 StartConnectTimeout();
Darin Petkova5e07ef2012-07-09 14:27:57 +0200748 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
749 // might be in hold state if the VPN connection was previously established
750 // successfully. The hold will be released by OnDefaultServiceChanged when a
751 // new default service connects. This ensures that the client will use a fully
752 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200753 if (device_) {
754 device_->OnDisconnected();
755 }
756 if (service_) {
757 service_->SetState(Service::kStateAssociating);
758 }
759}
760
Paul Stewart39964fa2012-04-04 09:50:25 -0700761string OpenVPNDriver::GetProviderType() const {
762 return flimflam::kProviderOpenVpn;
763}
764
Darin Petkovb536a742012-04-26 11:31:28 +0200765KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
766 SLOG(VPN, 2) << __func__;
767 KeyValueStore props = VPNDriver::GetProvider(error);
768 props.SetBool(flimflam::kPassphraseRequiredProperty,
769 args()->LookupString(
770 flimflam::kOpenVPNPasswordProperty, "").empty());
771 return props;
772}
773
Darin Petkov1a462de2012-05-02 11:10:48 +0200774// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
775// singleton if it's used outside OpenVPN.
776bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
777 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
778 string contents;
779 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
780 LOG(ERROR) << "Unable to read the lsb-release file: "
781 << lsb_release_file_.value();
782 return false;
783 }
784 vector<string> lines;
785 SplitString(contents, '\n', &lines);
786 for (vector<string>::const_iterator it = lines.begin();
787 it != lines.end(); ++it) {
788 size_t assign = it->find('=');
789 if (assign == string::npos) {
790 continue;
791 }
792 (*lsb_release)[it->substr(0, assign)] = it->substr(assign + 1);
793 }
794 return true;
795}
796
797void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
798 // Adds the platform name and version to the environment so that openvpn can
799 // send them to the server when OpenVPN.PushPeerInfo is set.
800 map<string, string> lsb_release;
801 ParseLSBRelease(&lsb_release);
802 string platform_name = lsb_release[kChromeOSReleaseName];
803 if (!platform_name.empty()) {
804 environment->push_back("IV_PLAT=" + platform_name);
805 }
806 string platform_version = lsb_release[kChromeOSReleaseVersion];
807 if (!platform_version.empty()) {
808 environment->push_back("IV_PLAT_REL=" + platform_version);
809 }
810}
811
Darin Petkova5e07ef2012-07-09 14:27:57 +0200812void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
813 SLOG(VPN, 2) << __func__
814 << "(" << (service ? service->friendly_name() : "-") << ")";
815 // Allow the openvpn client to connect/reconnect only over a connected
816 // underlying default service. If there's no default connected service, hold
817 // the openvpn client until an underlying connection is established. If the
818 // default service is our VPN service, hold the openvpn client on reconnect so
819 // that the VPN connection can be torn down fully before a new connection
820 // attempt is made over the underlying service.
821 if (service && service != service_ && service->IsConnected()) {
822 management_server_->ReleaseHold();
823 } else {
824 management_server_->Hold();
825 }
826}
827
Darin Petkov33af05c2012-02-28 10:10:30 +0100828} // namespace shill