blob: 75149d395538ea4ef7e86fe1ff4c8536681963a6 [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.
54const char kOpenVPNCertProperty[] = "OpenVPN.Cert";
55const char kOpenVPNKeyProperty[] = "OpenVPN.Key";
56const char kOpenVPNPingProperty[] = "OpenVPN.Ping";
57const char kOpenVPNPingExitProperty[] = "OpenVPN.PingExit";
58const char kOpenVPNPingRestartProperty[] = "OpenVPN.PingRestart";
59const char kOpenVPNTLSAuthProperty[] = "OpenVPN.TLSAuth";
60const char kOpenVPNVerbProperty[] = "OpenVPN.Verb";
61const char kVPNMTUProperty[] = "VPN.MTU";
Darin Petkov5a850472012-06-06 15:44:24 +020062
Paul Stewartebd38562012-03-23 13:06:40 -070063} // namespace
64
65// static
66const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
67// static
68const char OpenVPNDriver::kOpenVPNScript[] = SCRIPTDIR "/openvpn-script";
69// static
Darin Petkovd4325392012-04-23 15:48:22 +020070const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020071 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
72 { flimflam::kOpenVPNAuthProperty, 0 },
73 { flimflam::kOpenVPNAuthRetryProperty, 0 },
74 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
75 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
76 { flimflam::kOpenVPNCaCertProperty, 0 },
77 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020078 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020079 { flimflam::kOpenVPNCompLZOProperty, 0 },
80 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
81 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
82 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020083 { flimflam::kOpenVPNOTPProperty,
Darin Petkov02236552012-06-11 13:15:19 +020084 Property::kEphemeral | Property::kCredential | Property::kWriteOnly },
85 { flimflam::kOpenVPNPasswordProperty,
86 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020087 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020088 { flimflam::kOpenVPNPortProperty, 0 },
89 { flimflam::kOpenVPNProtoProperty, 0 },
90 { flimflam::kOpenVPNProviderProperty, 0 },
91 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
92 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
93 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
94 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
95 { flimflam::kOpenVPNRenegSecProperty, 0 },
96 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
97 { flimflam::kOpenVPNShaperProperty, 0 },
98 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
99 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
100 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
101 { flimflam::kOpenVPNUserProperty, 0 },
102 { flimflam::kProviderHostProperty, 0 },
103 { flimflam::kProviderNameProperty, 0 },
104 { flimflam::kProviderTypeProperty, 0 },
105 { kOpenVPNCertProperty, 0 },
106 { kOpenVPNKeyProperty, 0 },
107 { kOpenVPNPingExitProperty, 0 },
108 { kOpenVPNPingProperty, 0 },
109 { kOpenVPNPingRestartProperty, 0 },
110 { kOpenVPNTLSAuthProperty, 0 },
111 { kOpenVPNVerbProperty, 0 },
112 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700113
114 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200115 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100116};
Paul Stewart291a4732012-03-14 19:19:02 -0700117
Darin Petkov1a462de2012-05-02 11:10:48 +0200118// static
119const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
120// static
121const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
122//static
123const char OpenVPNDriver::kChromeOSReleaseVersion[] =
124 "CHROMEOS_RELEASE_VERSION";
125
Darin Petkova9b1fed2012-02-29 11:49:05 +0100126OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100127 EventDispatcher *dispatcher,
128 Metrics *metrics,
129 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800130 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700131 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +0200132 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200133 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100134 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800135 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100136 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200137 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200138 nss_(NSS::GetInstance()),
Darin Petkov5a850472012-06-06 15:44:24 +0200139 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200140 lsb_release_file_(kLSBReleaseFile),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100141 pid_(0),
Darin Petkova5e07ef2012-07-09 14:27:57 +0200142 child_watch_tag_(0),
143 default_service_callback_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100144
Darin Petkov36a3ace2012-03-06 17:22:14 +0100145OpenVPNDriver::~OpenVPNDriver() {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100146 Cleanup(Service::kStateIdle);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100147}
148
Darin Petkov3f9131c2012-03-20 11:37:32 +0100149void OpenVPNDriver::Cleanup(Service::ConnectState state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700150 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
151 << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200152 StopConnectTimeout();
Darin Petkov46463022012-03-29 14:57:32 +0200153 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200154 if (!tls_auth_file_.empty()) {
155 file_util::Delete(tls_auth_file_, false);
156 tls_auth_file_.clear();
157 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200158 if (default_service_callback_tag_) {
159 manager()->DeregisterDefaultServiceCallback(default_service_callback_tag_);
160 default_service_callback_tag_ = 0;
161 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100162 if (child_watch_tag_) {
163 glib_->SourceRemove(child_watch_tag_);
164 child_watch_tag_ = 0;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100165 }
166 rpc_task_.reset();
Darin Petkov5a850472012-06-06 15:44:24 +0200167 int interface_index = -1;
Darin Petkov36a3ace2012-03-06 17:22:14 +0100168 if (device_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200169 interface_index = device_->interface_index();
Darin Petkov029d3532012-04-18 14:38:04 +0200170 device_->OnDisconnected();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500171 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100172 device_ = NULL;
Darin Petkov5a850472012-06-06 15:44:24 +0200173 }
174 if (pid_) {
175 Closure callback;
176 if (interface_index >= 0) {
177 callback =
178 Bind(&DeleteInterface, device_info_->AsWeakPtr(), interface_index);
179 interface_index = -1;
180 }
181 process_killer_->Kill(pid_, callback);
182 pid_ = 0;
183 }
184 if (interface_index >= 0) {
Darin Petkov36a3ace2012-03-06 17:22:14 +0100185 device_info_->DeleteInterface(interface_index);
186 }
187 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100188 if (service_) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100189 service_->SetState(state);
Darin Petkov79d74c92012-03-07 17:20:32 +0100190 service_ = NULL;
191 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100192}
193
194bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700195 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100196
197 vector<string> options;
198 Error error;
199 InitOptions(&options, &error);
200 if (error.IsFailure()) {
201 return false;
202 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700203 SLOG(VPN, 2) << "OpenVPN process options: " << JoinString(options, ' ');
Darin Petkov36a3ace2012-03-06 17:22:14 +0100204
205 // TODO(petkov): This code needs to be abstracted away in a separate external
206 // process module (crosbug.com/27131).
207 vector<char *> process_args;
208 process_args.push_back(const_cast<char *>(kOpenVPNPath));
209 for (vector<string>::const_iterator it = options.begin();
210 it != options.end(); ++it) {
211 process_args.push_back(const_cast<char *>(it->c_str()));
212 }
213 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200214
215 vector<string> environment;
216 InitEnvironment(&environment);
217
218 vector<char *> process_env;
219 for (vector<string>::const_iterator it = environment.begin();
220 it != environment.end(); ++it) {
221 process_env.push_back(const_cast<char *>(it->c_str()));
222 }
223 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100224
225 CHECK(!pid_);
226 // Redirect all openvpn output to stderr.
227 int stderr_fd = fileno(stderr);
228 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
Darin Petkov1a462de2012-05-02 11:10:48 +0200229 process_env.data(),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100230 G_SPAWN_DO_NOT_REAP_CHILD,
231 NULL,
232 NULL,
233 &pid_,
234 NULL,
235 &stderr_fd,
236 &stderr_fd,
237 NULL)) {
238 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
239 return false;
240 }
241 CHECK(!child_watch_tag_);
242 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
243 return true;
244}
245
246// static
247void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700248 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100249 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
250 me->child_watch_tag_ = 0;
251 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200252 me->pid_ = 0;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100253 me->Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100254 // TODO(petkov): Figure if we need to restart the connection.
255}
Darin Petkov33af05c2012-02-28 10:10:30 +0100256
Darin Petkov5a850472012-06-06 15:44:24 +0200257// static
Darin Petkov5dbd2612012-06-07 16:22:16 +0200258void OpenVPNDriver::DeleteInterface(const WeakPtr<DeviceInfo> &device_info,
Darin Petkov5a850472012-06-06 15:44:24 +0200259 int interface_index) {
260 if (device_info) {
261 LOG(INFO) << "Deleting interface " << interface_index;
262 device_info->DeleteInterface(interface_index);
263 }
264}
265
Paul Stewartca6abd42012-03-01 15:45:29 -0800266bool OpenVPNDriver::ClaimInterface(const string &link_name,
267 int interface_index) {
268 if (link_name != tunnel_interface_) {
269 return false;
270 }
271
Ben Chanfad4a0b2012-04-18 15:49:59 -0700272 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800273
Darin Petkovf20994f2012-03-05 16:12:19 +0100274 CHECK(!device_);
Darin Petkov602303f2012-06-06 12:15:59 +0200275 device_ = new VPN(control_, dispatcher(), metrics_, manager(),
Darin Petkovf20994f2012-03-05 16:12:19 +0100276 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500277 device_->SetEnabled(true);
Darin Petkov79d74c92012-03-07 17:20:32 +0100278 device_->SelectService(service_);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500279
Darin Petkov36a3ace2012-03-06 17:22:14 +0100280 rpc_task_.reset(new RPCTask(control_, this));
281 if (!SpawnOpenVPN()) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100282 Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100283 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200284 default_service_callback_tag_ =
285 manager()->RegisterDefaultServiceCallback(
286 Bind(&OpenVPNDriver::OnDefaultServiceChanged, Unretained(this)));
Paul Stewartca6abd42012-03-01 15:45:29 -0800287 return true;
288}
289
Darin Petkov209e6292012-04-20 11:33:32 +0200290void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
291 NOTREACHED();
292}
293
Darin Petkov36a3ace2012-03-06 17:22:14 +0100294void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100295 const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200296 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100297 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100298 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100299 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100300 }
301 IPConfig::Properties properties;
Darin Petkove8587e32012-07-02 13:56:07 +0200302 if (device_->ipconfig()) {
303 // On restart/reconnect, update the existing IP configuration.
304 properties = device_->ipconfig()->properties();
305 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100306 ParseIPConfiguration(dict, &properties);
Darin Petkov79d74c92012-03-07 17:20:32 +0100307 device_->UpdateIPConfig(properties);
Darin Petkov602303f2012-06-06 12:15:59 +0200308 StopConnectTimeout();
Darin Petkov14c29ec2012-03-02 11:34:19 +0100309}
310
311// static
312void OpenVPNDriver::ParseIPConfiguration(
313 const map<string, string> &configuration,
314 IPConfig::Properties *properties) {
315 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100316 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100317 properties->address_family = IPAddress::kFamilyIPv4;
Darin Petkove8587e32012-07-02 13:56:07 +0200318 if (!properties->subnet_prefix) {
319 properties->subnet_prefix =
320 IPAddress::GetMaxPrefixLength(properties->address_family);
321 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100322 for (map<string, string>::const_iterator it = configuration.begin();
323 it != configuration.end(); ++it) {
324 const string &key = it->first;
325 const string &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700326 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100327 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
328 properties->address = value;
329 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
330 properties->broadcast_address = value;
331 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700332 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100333 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
334 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
335 properties->peer_address = value;
336 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
337 properties->gateway = value;
338 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700339 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100340 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
341 int mtu = 0;
342 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
343 properties->mtu = mtu;
344 } else {
345 LOG(ERROR) << "MTU " << value << " ignored.";
346 }
347 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
348 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
349 int order = 0;
350 if (base::StringToInt(suffix, &order)) {
351 foreign_options[order] = value;
352 } else {
353 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
354 }
355 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100356 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
357 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100358 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700359 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100360 }
361 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100362 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100363 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100364}
365
366// static
367void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
368 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200369 vector<string> domain_search;
370 vector<string> dns_servers;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100371 for (ForeignOptions::const_iterator it = options.begin();
372 it != options.end(); ++it) {
Darin Petkove8587e32012-07-02 13:56:07 +0200373 ParseForeignOption(it->second, &domain_search, &dns_servers);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100374 }
Darin Petkove8587e32012-07-02 13:56:07 +0200375 if (!domain_search.empty()) {
376 properties->domain_search.swap(domain_search);
377 }
378 LOG_IF(WARNING, properties->domain_search.empty())
379 << "No search domains provided.";
380 if (!dns_servers.empty()) {
381 properties->dns_servers.swap(dns_servers);
382 }
383 LOG_IF(WARNING, properties->dns_servers.empty())
384 << "No DNS servers provided.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100385}
386
387// static
388void OpenVPNDriver::ParseForeignOption(const string &option,
Darin Petkove8587e32012-07-02 13:56:07 +0200389 vector<string> *domain_search,
390 vector<string> *dns_servers) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700391 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100392 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200393 SplitString(option, ' ', &tokens);
394 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100395 return;
396 }
397 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200398 domain_search->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100399 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
Darin Petkove8587e32012-07-02 13:56:07 +0200400 dns_servers->push_back(tokens[2]);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100401 }
402}
403
Darin Petkov60596742012-03-05 12:17:17 +0100404// static
405IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
406 const string &prefix, const string &key, RouteOptions *routes) {
407 int order = 0;
408 if (!StartsWithASCII(key, prefix, false) ||
409 !base::StringToInt(key.substr(prefix.size()), &order)) {
410 return NULL;
411 }
412 return &(*routes)[order];
413}
414
415// static
416void OpenVPNDriver::ParseRouteOption(
417 const string &key, const string &value, RouteOptions *routes) {
418 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
419 if (route) {
420 route->host = value;
421 return;
422 }
423 route = GetRouteOptionEntry("netmask_", key, routes);
424 if (route) {
425 route->netmask = value;
426 return;
427 }
428 route = GetRouteOptionEntry("gateway_", key, routes);
429 if (route) {
430 route->gateway = value;
431 return;
432 }
433 LOG(WARNING) << "Unknown route option ignored: " << key;
434}
435
436// static
437void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
438 IPConfig::Properties *properties) {
Darin Petkove8587e32012-07-02 13:56:07 +0200439 vector<IPConfig::Route> new_routes;
Darin Petkov60596742012-03-05 12:17:17 +0100440 for (RouteOptions::const_iterator it = routes.begin();
441 it != routes.end(); ++it) {
442 const IPConfig::Route &route = it->second;
443 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
444 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
445 continue;
446 }
Darin Petkove8587e32012-07-02 13:56:07 +0200447 new_routes.push_back(route);
Darin Petkov60596742012-03-05 12:17:17 +0100448 }
Darin Petkove8587e32012-07-02 13:56:07 +0200449 if (!new_routes.empty()) {
450 properties->routes.swap(new_routes);
451 }
452 LOG_IF(WARNING, properties->routes.empty()) << "No routes provided.";
Darin Petkov60596742012-03-05 12:17:17 +0100453}
454
Darin Petkov602303f2012-06-06 12:15:59 +0200455void OpenVPNDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
456 StartConnectTimeout();
Darin Petkov79d74c92012-03-07 17:20:32 +0100457 service_ = service;
458 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100459 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
460 Error::PopulateAndLog(
461 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov3f9131c2012-03-20 11:37:32 +0100462 Cleanup(Service::kStateFailure);
Darin Petkovf20994f2012-03-05 16:12:19 +0100463 }
464 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100465}
466
Darin Petkovfe6a9372012-02-28 16:25:06 +0100467void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200468 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100469 if (vpnhost.empty()) {
470 Error::PopulateAndLog(
471 error, Error::kInvalidArguments, "VPN host not specified.");
472 return;
473 }
474 options->push_back("--client");
475 options->push_back("--tls-client");
476 options->push_back("--remote");
477 options->push_back(vpnhost);
478 options->push_back("--nobind");
479 options->push_back("--persist-key");
480 options->push_back("--persist-tun");
481
Darin Petkovf20994f2012-03-05 16:12:19 +0100482 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800483 options->push_back("--dev");
484 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100485 options->push_back("--dev-type");
486 options->push_back("tun");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100487
Darin Petkov55771b72012-04-25 09:25:19 +0200488 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100489
Darin Petkovf3c71d72012-03-21 12:32:15 +0100490 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100491 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
492 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100493 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200494 {
495 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200496 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200497 if (!contents.empty()) {
498 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
499 file_util::WriteFile(
500 tls_auth_file_, contents.data(), contents.size()) !=
501 static_cast<int>(contents.size())) {
502 Error::PopulateAndLog(
503 error, Error::kInternalError, "Unable to setup tls-auth file.");
504 return;
505 }
506 options->push_back("--tls-auth");
507 options->push_back(tls_auth_file_.value());
508 }
509 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100510 AppendValueOption(
511 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
512 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
513 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
514 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
515 AppendValueOption(
516 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
517 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
518 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
519 AppendFlag(
520 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
521 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
522 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
523 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
524 "--server-poll-timeout", options);
525
Darin Petkove0d5dd12012-04-04 16:10:48 +0200526 if (!InitNSSOptions(options, error)) {
527 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200528 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100529
530 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100531 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
532 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
533 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100534
535 AppendValueOption(flimflam::kOpenVPNCaCertProperty, "--ca", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100536 AppendValueOption(kOpenVPNCertProperty, "--cert", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100537 AppendValueOption(
538 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100539 AppendValueOption(kOpenVPNKeyProperty, "--key", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100540
Darin Petkove0d5dd12012-04-04 16:10:48 +0200541 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100542
543 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100544 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200545 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100546 if (remote_cert_tls.empty()) {
547 remote_cert_tls = "server";
548 }
549 if (remote_cert_tls != "none") {
550 options->push_back("--remote-cert-tls");
551 options->push_back(remote_cert_tls);
552 }
553
554 // This is an undocumented command line argument that works like a .cfg file
555 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
556 AppendValueOption(
557 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
558 // TODO(sleffler): Support more than one eku parameter.
559 AppendValueOption(
560 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
561 AppendValueOption(
562 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
563
Darin Petkove0d5dd12012-04-04 16:10:48 +0200564 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200565 return;
566 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100567
Darin Petkova9b1fed2012-02-29 11:49:05 +0100568 // Setup openvpn-script options and RPC information required to send back
569 // Layer 3 configuration.
570 options->push_back("--setenv");
571 options->push_back("CONNMAN_BUSNAME");
572 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
573 options->push_back("--setenv");
574 options->push_back("CONNMAN_INTERFACE");
575 options->push_back(rpc_task_->GetRpcInterfaceIdentifier());
576 options->push_back("--setenv");
577 options->push_back("CONNMAN_PATH");
578 options->push_back(rpc_task_->GetRpcIdentifier());
579 options->push_back("--script-security");
580 options->push_back("2");
581 options->push_back("--up");
582 options->push_back(kOpenVPNScript);
583 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100584
585 // Disable openvpn handling since we do route+ifconfig work.
586 options->push_back("--route-noexec");
587 options->push_back("--ifconfig-noexec");
588
589 // Drop root privileges on connection and enable callback scripts to send
590 // notify messages.
591 options->push_back("--user");
592 options->push_back("openvpn");
593 options->push_back("--group");
594 options->push_back("openvpn");
595}
596
Darin Petkove0d5dd12012-04-04 16:10:48 +0200597bool OpenVPNDriver::InitNSSOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200598 string ca_cert =
599 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200600 if (!ca_cert.empty()) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200601 if (!args()->LookupString(flimflam::kOpenVPNCaCertProperty, "").empty()) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200602 Error::PopulateAndLog(error,
603 Error::kInvalidArguments,
604 "Can't specify both CACert and CACertNSS.");
605 return false;
606 }
Darin Petkovb451d6e2012-04-23 11:56:41 +0200607 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200608 vector<char> id(vpnhost.begin(), vpnhost.end());
609 FilePath certfile = nss_->GetPEMCertfile(ca_cert, id);
610 if (certfile.empty()) {
611 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
612 } else {
613 options->push_back("--ca");
614 options->push_back(certfile.value());
615 }
616 }
617 return true;
618}
619
620void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200621 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200622 if (!id.empty()) {
623 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200624 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200625 if (provider.empty()) {
626 provider = kDefaultPKCS11Provider;
627 }
628 options->push_back("--pkcs11-providers");
629 options->push_back(provider);
630 options->push_back("--pkcs11-id");
631 options->push_back(id);
632 }
633}
634
635bool OpenVPNDriver::InitManagementChannelOptions(
636 vector<string> *options, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +0200637 if (!management_server_->Start(dispatcher(), &sockets_, options)) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200638 Error::PopulateAndLog(
639 error, Error::kInternalError, "Unable to setup management channel.");
640 return false;
641 }
Darin Petkova5e07ef2012-07-09 14:27:57 +0200642 // If there's a connected default service already, allow the openvpn client to
643 // establish connection as soon as it's started. Otherwise, hold the client
644 // until an underlying service connects and OnDefaultServiceChanged is
645 // invoked.
646 ServiceRefPtr service = manager()->GetDefaultService();
647 if (service && service->IsConnected()) {
648 management_server_->ReleaseHold();
649 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200650 return true;
651}
652
Darin Petkov55771b72012-04-25 09:25:19 +0200653void OpenVPNDriver::InitLoggingOptions(vector<string> *options) {
654 options->push_back("--syslog");
655
656 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
657 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
658 verb = "3";
659 }
660 if (!verb.empty()) {
661 options->push_back("--verb");
662 options->push_back(verb);
663 }
664}
665
Darin Petkov46463022012-03-29 14:57:32 +0200666bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100667 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200668 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100669 if (!value.empty()) {
670 options->push_back(option);
671 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200672 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100673 }
Darin Petkov46463022012-03-29 14:57:32 +0200674 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100675}
676
Darin Petkov46463022012-03-29 14:57:32 +0200677bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100678 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200679 if (args()->ContainsString(property)) {
Darin Petkovfe6a9372012-02-28 16:25:06 +0100680 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200681 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100682 }
Darin Petkov46463022012-03-29 14:57:32 +0200683 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100684}
685
Darin Petkov6aa21872012-03-09 16:10:19 +0100686void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700687 SLOG(VPN, 2) << __func__;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100688 Cleanup(Service::kStateIdle);
Darin Petkov6aa21872012-03-09 16:10:19 +0100689}
690
Darin Petkov5eb05422012-05-11 15:45:25 +0200691void OpenVPNDriver::OnConnectionDisconnected() {
Darin Petkov602303f2012-06-06 12:15:59 +0200692 LOG(ERROR) << "VPN connection disconnected.";
Darin Petkov5eb05422012-05-11 15:45:25 +0200693 Cleanup(Service::kStateFailure);
694}
695
Darin Petkov271fe522012-03-27 13:47:29 +0200696void OpenVPNDriver::OnReconnecting() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700697 SLOG(VPN, 2) << __func__;
Darin Petkov602303f2012-06-06 12:15:59 +0200698 StartConnectTimeout();
Darin Petkova5e07ef2012-07-09 14:27:57 +0200699 // On restart/reconnect, drop the VPN connection, if any. The openvpn client
700 // might be in hold state if the VPN connection was previously established
701 // successfully. The hold will be released by OnDefaultServiceChanged when a
702 // new default service connects. This ensures that the client will use a fully
703 // functional underlying connection to reconnect.
Darin Petkov271fe522012-03-27 13:47:29 +0200704 if (device_) {
705 device_->OnDisconnected();
706 }
707 if (service_) {
708 service_->SetState(Service::kStateAssociating);
709 }
710}
711
Paul Stewart39964fa2012-04-04 09:50:25 -0700712string OpenVPNDriver::GetProviderType() const {
713 return flimflam::kProviderOpenVpn;
714}
715
Darin Petkovb536a742012-04-26 11:31:28 +0200716KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
717 SLOG(VPN, 2) << __func__;
718 KeyValueStore props = VPNDriver::GetProvider(error);
719 props.SetBool(flimflam::kPassphraseRequiredProperty,
720 args()->LookupString(
721 flimflam::kOpenVPNPasswordProperty, "").empty());
722 return props;
723}
724
Darin Petkov1a462de2012-05-02 11:10:48 +0200725// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
726// singleton if it's used outside OpenVPN.
727bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
728 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
729 string contents;
730 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
731 LOG(ERROR) << "Unable to read the lsb-release file: "
732 << lsb_release_file_.value();
733 return false;
734 }
735 vector<string> lines;
736 SplitString(contents, '\n', &lines);
737 for (vector<string>::const_iterator it = lines.begin();
738 it != lines.end(); ++it) {
739 size_t assign = it->find('=');
740 if (assign == string::npos) {
741 continue;
742 }
743 (*lsb_release)[it->substr(0, assign)] = it->substr(assign + 1);
744 }
745 return true;
746}
747
748void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
749 // Adds the platform name and version to the environment so that openvpn can
750 // send them to the server when OpenVPN.PushPeerInfo is set.
751 map<string, string> lsb_release;
752 ParseLSBRelease(&lsb_release);
753 string platform_name = lsb_release[kChromeOSReleaseName];
754 if (!platform_name.empty()) {
755 environment->push_back("IV_PLAT=" + platform_name);
756 }
757 string platform_version = lsb_release[kChromeOSReleaseVersion];
758 if (!platform_version.empty()) {
759 environment->push_back("IV_PLAT_REL=" + platform_version);
760 }
761}
762
Darin Petkova5e07ef2012-07-09 14:27:57 +0200763void OpenVPNDriver::OnDefaultServiceChanged(const ServiceRefPtr &service) {
764 SLOG(VPN, 2) << __func__
765 << "(" << (service ? service->friendly_name() : "-") << ")";
766 // Allow the openvpn client to connect/reconnect only over a connected
767 // underlying default service. If there's no default connected service, hold
768 // the openvpn client until an underlying connection is established. If the
769 // default service is our VPN service, hold the openvpn client on reconnect so
770 // that the VPN connection can be torn down fully before a new connection
771 // attempt is made over the underlying service.
772 if (service && service != service_ && service->IsConnected()) {
773 management_server_->ReleaseHold();
774 } else {
775 management_server_->Hold();
776 }
777}
778
Darin Petkov33af05c2012-02-28 10:10:30 +0100779} // namespace shill