blob: 33d1144e2926ca5a09c2786e91071db12cc396e4 [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 Petkovfe6a9372012-02-28 16:25:06 +010010#include <base/logging.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010011#include <base/string_number_conversions.h>
Darin Petkov78f63262012-03-26 01:30:24 +020012#include <base/string_split.h>
Darin Petkov14c29ec2012-03-02 11:34:19 +010013#include <base/string_util.h>
Darin Petkovfe6a9372012-02-28 16:25:06 +010014#include <chromeos/dbus/service_constants.h>
15
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"
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 Petkova9b1fed2012-02-29 11:49:05 +010023#include "shill/rpc_task.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070024#include "shill/scope_logger.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 Petkov78f63262012-03-26 01:30:24 +020029using base::SplitString;
Darin Petkov14c29ec2012-03-02 11:34:19 +010030using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010031using std::string;
32using std::vector;
33
Darin Petkov33af05c2012-02-28 10:10:30 +010034namespace shill {
35
Darin Petkova9b1fed2012-02-29 11:49:05 +010036namespace {
Darin Petkov14c29ec2012-03-02 11:34:19 +010037const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
38const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
39const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
40const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
41const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
42const char kOpenVPNRouteOptionPrefix[] = "route_";
43const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
44const char kOpenVPNTrustedIP[] = "trusted_ip";
45const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010046
Darin Petkove0d5dd12012-04-04 16:10:48 +020047const char kDefaultPKCS11Provider[] = "libchaps.so";
48
Darin Petkovf3c71d72012-03-21 12:32:15 +010049// TODO(petkov): Move to chromeos/dbus/service_constants.h.
50const char kOpenVPNCertProperty[] = "OpenVPN.Cert";
51const char kOpenVPNKeyProperty[] = "OpenVPN.Key";
52const char kOpenVPNPingProperty[] = "OpenVPN.Ping";
53const char kOpenVPNPingExitProperty[] = "OpenVPN.PingExit";
54const char kOpenVPNPingRestartProperty[] = "OpenVPN.PingRestart";
55const char kOpenVPNTLSAuthProperty[] = "OpenVPN.TLSAuth";
56const char kOpenVPNVerbProperty[] = "OpenVPN.Verb";
57const char kVPNMTUProperty[] = "VPN.MTU";
Paul Stewartebd38562012-03-23 13:06:40 -070058} // namespace
59
60// static
61const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
62// static
63const char OpenVPNDriver::kOpenVPNScript[] = SCRIPTDIR "/openvpn-script";
64// static
Darin Petkovd4325392012-04-23 15:48:22 +020065const VPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020066 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
67 { flimflam::kOpenVPNAuthProperty, 0 },
68 { flimflam::kOpenVPNAuthRetryProperty, 0 },
69 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
70 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
71 { flimflam::kOpenVPNCaCertProperty, 0 },
72 { flimflam::kOpenVPNCipherProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020073 { flimflam::kOpenVPNClientCertIdProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020074 { flimflam::kOpenVPNCompLZOProperty, 0 },
75 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
76 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
77 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020078 { flimflam::kOpenVPNOTPProperty,
79 Property::kEphemeral | Property::kCredential | Property::kWriteOnly},
80 { flimflam::kOpenVPNPasswordProperty, Property::kCredential },
81 { flimflam::kOpenVPNPinProperty, Property::kCredential },
Darin Petkov1847d792012-04-17 11:33:06 +020082 { flimflam::kOpenVPNPortProperty, 0 },
83 { flimflam::kOpenVPNProtoProperty, 0 },
84 { flimflam::kOpenVPNProviderProperty, 0 },
85 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
86 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
87 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
88 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
89 { flimflam::kOpenVPNRenegSecProperty, 0 },
90 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
91 { flimflam::kOpenVPNShaperProperty, 0 },
92 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
93 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
94 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
95 { flimflam::kOpenVPNUserProperty, 0 },
96 { flimflam::kProviderHostProperty, 0 },
97 { flimflam::kProviderNameProperty, 0 },
98 { flimflam::kProviderTypeProperty, 0 },
99 { kOpenVPNCertProperty, 0 },
100 { kOpenVPNKeyProperty, 0 },
101 { kOpenVPNPingExitProperty, 0 },
102 { kOpenVPNPingProperty, 0 },
103 { kOpenVPNPingRestartProperty, 0 },
104 { kOpenVPNTLSAuthProperty, 0 },
105 { kOpenVPNVerbProperty, 0 },
106 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700107
108 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200109 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100110};
Paul Stewart291a4732012-03-14 19:19:02 -0700111
Darin Petkov1a462de2012-05-02 11:10:48 +0200112// static
113const char OpenVPNDriver::kLSBReleaseFile[] = "/etc/lsb-release";
114// static
115const char OpenVPNDriver::kChromeOSReleaseName[] = "CHROMEOS_RELEASE_NAME";
116//static
117const char OpenVPNDriver::kChromeOSReleaseVersion[] =
118 "CHROMEOS_RELEASE_VERSION";
119
Darin Petkova9b1fed2012-02-29 11:49:05 +0100120OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100121 EventDispatcher *dispatcher,
122 Metrics *metrics,
123 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800124 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700125 GLib *glib)
Darin Petkov0e9735d2012-04-24 12:33:45 +0200126 : VPNDriver(manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +0200127 control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100128 dispatcher_(dispatcher),
129 metrics_(metrics),
Paul Stewartca6abd42012-03-01 15:45:29 -0800130 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100131 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200132 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200133 nss_(NSS::GetInstance()),
Darin Petkov1a462de2012-05-02 11:10:48 +0200134 lsb_release_file_(kLSBReleaseFile),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100135 pid_(0),
136 child_watch_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100137
Darin Petkov36a3ace2012-03-06 17:22:14 +0100138OpenVPNDriver::~OpenVPNDriver() {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100139 Cleanup(Service::kStateIdle);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100140}
141
Darin Petkov3f9131c2012-03-20 11:37:32 +0100142void OpenVPNDriver::Cleanup(Service::ConnectState state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700143 SLOG(VPN, 2) << __func__ << "(" << Service::ConnectStateToString(state)
144 << ")";
Darin Petkov46463022012-03-29 14:57:32 +0200145 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200146 if (!tls_auth_file_.empty()) {
147 file_util::Delete(tls_auth_file_, false);
148 tls_auth_file_.clear();
149 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100150 if (child_watch_tag_) {
151 glib_->SourceRemove(child_watch_tag_);
152 child_watch_tag_ = 0;
153 CHECK(pid_);
154 kill(pid_, SIGTERM);
155 }
156 if (pid_) {
157 glib_->SpawnClosePID(pid_);
158 pid_ = 0;
159 }
160 rpc_task_.reset();
161 if (device_) {
162 int interface_index = device_->interface_index();
Darin Petkov029d3532012-04-18 14:38:04 +0200163 device_->OnDisconnected();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500164 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100165 device_ = NULL;
166 device_info_->DeleteInterface(interface_index);
167 }
168 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100169 if (service_) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100170 service_->SetState(state);
Darin Petkov79d74c92012-03-07 17:20:32 +0100171 service_ = NULL;
172 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100173}
174
175bool OpenVPNDriver::SpawnOpenVPN() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700176 SLOG(VPN, 2) << __func__ << "(" << tunnel_interface_ << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100177
178 vector<string> options;
179 Error error;
180 InitOptions(&options, &error);
181 if (error.IsFailure()) {
182 return false;
183 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700184 SLOG(VPN, 2) << "OpenVPN process options: " << JoinString(options, ' ');
Darin Petkov36a3ace2012-03-06 17:22:14 +0100185
186 // TODO(petkov): This code needs to be abstracted away in a separate external
187 // process module (crosbug.com/27131).
188 vector<char *> process_args;
189 process_args.push_back(const_cast<char *>(kOpenVPNPath));
190 for (vector<string>::const_iterator it = options.begin();
191 it != options.end(); ++it) {
192 process_args.push_back(const_cast<char *>(it->c_str()));
193 }
194 process_args.push_back(NULL);
Darin Petkov1a462de2012-05-02 11:10:48 +0200195
196 vector<string> environment;
197 InitEnvironment(&environment);
198
199 vector<char *> process_env;
200 for (vector<string>::const_iterator it = environment.begin();
201 it != environment.end(); ++it) {
202 process_env.push_back(const_cast<char *>(it->c_str()));
203 }
204 process_env.push_back(NULL);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100205
206 CHECK(!pid_);
207 // Redirect all openvpn output to stderr.
208 int stderr_fd = fileno(stderr);
209 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
Darin Petkov1a462de2012-05-02 11:10:48 +0200210 process_env.data(),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100211 G_SPAWN_DO_NOT_REAP_CHILD,
212 NULL,
213 NULL,
214 &pid_,
215 NULL,
216 &stderr_fd,
217 &stderr_fd,
218 NULL)) {
219 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
220 return false;
221 }
222 CHECK(!child_watch_tag_);
223 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
224 return true;
225}
226
227// static
228void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700229 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
Darin Petkov36a3ace2012-03-06 17:22:14 +0100230 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
231 me->child_watch_tag_ = 0;
232 CHECK_EQ(pid, me->pid_);
Darin Petkov3f9131c2012-03-20 11:37:32 +0100233 me->Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100234 // TODO(petkov): Figure if we need to restart the connection.
235}
Darin Petkov33af05c2012-02-28 10:10:30 +0100236
Paul Stewartca6abd42012-03-01 15:45:29 -0800237bool OpenVPNDriver::ClaimInterface(const string &link_name,
238 int interface_index) {
239 if (link_name != tunnel_interface_) {
240 return false;
241 }
242
Ben Chanfad4a0b2012-04-18 15:49:59 -0700243 SLOG(VPN, 2) << "Claiming " << link_name << " for OpenVPN tunnel";
Paul Stewartca6abd42012-03-01 15:45:29 -0800244
Darin Petkovf20994f2012-03-05 16:12:19 +0100245 CHECK(!device_);
Darin Petkov0e9735d2012-04-24 12:33:45 +0200246 device_ = new VPN(control_, dispatcher_, metrics_, manager(),
Darin Petkovf20994f2012-03-05 16:12:19 +0100247 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500248
249 device_->SetEnabled(true);
Darin Petkov79d74c92012-03-07 17:20:32 +0100250 device_->SelectService(service_);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500251
Darin Petkov36a3ace2012-03-06 17:22:14 +0100252 rpc_task_.reset(new RPCTask(control_, this));
253 if (!SpawnOpenVPN()) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100254 Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100255 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800256 return true;
257}
258
Darin Petkov209e6292012-04-20 11:33:32 +0200259void OpenVPNDriver::GetLogin(string */*user*/, string */*password*/) {
260 NOTREACHED();
261}
262
Darin Petkov36a3ace2012-03-06 17:22:14 +0100263void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100264 const map<string, string> &dict) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700265 SLOG(VPN, 2) << __func__ << "(" << reason << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100266 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100267 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100268 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100269 }
270 IPConfig::Properties properties;
271 ParseIPConfiguration(dict, &properties);
Darin Petkov79d74c92012-03-07 17:20:32 +0100272 device_->UpdateIPConfig(properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100273}
274
275// static
276void OpenVPNDriver::ParseIPConfiguration(
277 const map<string, string> &configuration,
278 IPConfig::Properties *properties) {
279 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100280 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100281 properties->address_family = IPAddress::kFamilyIPv4;
Paul Stewart48100b02012-03-19 07:53:52 -0700282 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
283 properties->address_family);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100284 for (map<string, string>::const_iterator it = configuration.begin();
285 it != configuration.end(); ++it) {
286 const string &key = it->first;
287 const string &value = it->second;
Ben Chanfad4a0b2012-04-18 15:49:59 -0700288 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100289 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
290 properties->address = value;
291 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
292 properties->broadcast_address = value;
293 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700294 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100295 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
296 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
297 properties->peer_address = value;
298 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
299 properties->gateway = value;
300 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700301 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100302 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
303 int mtu = 0;
304 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
305 properties->mtu = mtu;
306 } else {
307 LOG(ERROR) << "MTU " << value << " ignored.";
308 }
309 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
310 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
311 int order = 0;
312 if (base::StringToInt(suffix, &order)) {
313 foreign_options[order] = value;
314 } else {
315 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
316 }
317 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100318 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
319 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100320 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700321 SLOG(VPN, 2) << "Key ignored.";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100322 }
323 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100324 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100325 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100326}
327
328// static
329void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
330 IPConfig::Properties *properties) {
331 for (ForeignOptions::const_iterator it = options.begin();
332 it != options.end(); ++it) {
333 ParseForeignOption(it->second, properties);
334 }
335}
336
337// static
338void OpenVPNDriver::ParseForeignOption(const string &option,
339 IPConfig::Properties *properties) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700340 SLOG(VPN, 2) << __func__ << "(" << option << ")";
Darin Petkov14c29ec2012-03-02 11:34:19 +0100341 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200342 SplitString(option, ' ', &tokens);
343 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100344 return;
345 }
346 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
347 properties->domain_search.push_back(tokens[2]);
348 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
349 properties->dns_servers.push_back(tokens[2]);
350 }
351}
352
Darin Petkov60596742012-03-05 12:17:17 +0100353// static
354IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
355 const string &prefix, const string &key, RouteOptions *routes) {
356 int order = 0;
357 if (!StartsWithASCII(key, prefix, false) ||
358 !base::StringToInt(key.substr(prefix.size()), &order)) {
359 return NULL;
360 }
361 return &(*routes)[order];
362}
363
364// static
365void OpenVPNDriver::ParseRouteOption(
366 const string &key, const string &value, RouteOptions *routes) {
367 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
368 if (route) {
369 route->host = value;
370 return;
371 }
372 route = GetRouteOptionEntry("netmask_", key, routes);
373 if (route) {
374 route->netmask = value;
375 return;
376 }
377 route = GetRouteOptionEntry("gateway_", key, routes);
378 if (route) {
379 route->gateway = value;
380 return;
381 }
382 LOG(WARNING) << "Unknown route option ignored: " << key;
383}
384
385// static
386void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
387 IPConfig::Properties *properties) {
388 for (RouteOptions::const_iterator it = routes.begin();
389 it != routes.end(); ++it) {
390 const IPConfig::Route &route = it->second;
391 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
392 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
393 continue;
394 }
395 properties->routes.push_back(route);
396 }
397}
398
Darin Petkov79d74c92012-03-07 17:20:32 +0100399void OpenVPNDriver::Connect(const VPNServiceRefPtr &service,
400 Error *error) {
401 service_ = service;
402 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100403 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
404 Error::PopulateAndLog(
405 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov3f9131c2012-03-20 11:37:32 +0100406 Cleanup(Service::kStateFailure);
Darin Petkovf20994f2012-03-05 16:12:19 +0100407 }
408 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100409}
410
Darin Petkovfe6a9372012-02-28 16:25:06 +0100411void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200412 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100413 if (vpnhost.empty()) {
414 Error::PopulateAndLog(
415 error, Error::kInvalidArguments, "VPN host not specified.");
416 return;
417 }
418 options->push_back("--client");
419 options->push_back("--tls-client");
420 options->push_back("--remote");
421 options->push_back(vpnhost);
422 options->push_back("--nobind");
423 options->push_back("--persist-key");
424 options->push_back("--persist-tun");
425
Darin Petkovf20994f2012-03-05 16:12:19 +0100426 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800427 options->push_back("--dev");
428 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100429 options->push_back("--dev-type");
430 options->push_back("tun");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100431
Darin Petkov55771b72012-04-25 09:25:19 +0200432 InitLoggingOptions(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100433
Darin Petkovf3c71d72012-03-21 12:32:15 +0100434 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100435 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
436 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100437 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200438 {
439 string contents =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200440 args()->LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
Darin Petkov1fa81942012-04-02 11:38:08 +0200441 if (!contents.empty()) {
442 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
443 file_util::WriteFile(
444 tls_auth_file_, contents.data(), contents.size()) !=
445 static_cast<int>(contents.size())) {
446 Error::PopulateAndLog(
447 error, Error::kInternalError, "Unable to setup tls-auth file.");
448 return;
449 }
450 options->push_back("--tls-auth");
451 options->push_back(tls_auth_file_.value());
452 }
453 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100454 AppendValueOption(
455 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
456 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
457 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
458 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
459 AppendValueOption(
460 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
461 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
462 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
463 AppendFlag(
464 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
465 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
466 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
467 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
468 "--server-poll-timeout", options);
469
Darin Petkove0d5dd12012-04-04 16:10:48 +0200470 if (!InitNSSOptions(options, error)) {
471 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200472 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100473
474 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100475 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
476 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
477 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100478
479 AppendValueOption(flimflam::kOpenVPNCaCertProperty, "--ca", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100480 AppendValueOption(kOpenVPNCertProperty, "--cert", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100481 AppendValueOption(
482 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100483 AppendValueOption(kOpenVPNKeyProperty, "--key", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100484
Darin Petkove0d5dd12012-04-04 16:10:48 +0200485 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100486
487 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100488 string remote_cert_tls =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200489 args()->LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100490 if (remote_cert_tls.empty()) {
491 remote_cert_tls = "server";
492 }
493 if (remote_cert_tls != "none") {
494 options->push_back("--remote-cert-tls");
495 options->push_back(remote_cert_tls);
496 }
497
498 // This is an undocumented command line argument that works like a .cfg file
499 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
500 AppendValueOption(
501 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
502 // TODO(sleffler): Support more than one eku parameter.
503 AppendValueOption(
504 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
505 AppendValueOption(
506 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
507
Darin Petkove0d5dd12012-04-04 16:10:48 +0200508 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200509 return;
510 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100511
Darin Petkova9b1fed2012-02-29 11:49:05 +0100512 // Setup openvpn-script options and RPC information required to send back
513 // Layer 3 configuration.
514 options->push_back("--setenv");
515 options->push_back("CONNMAN_BUSNAME");
516 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
517 options->push_back("--setenv");
518 options->push_back("CONNMAN_INTERFACE");
519 options->push_back(rpc_task_->GetRpcInterfaceIdentifier());
520 options->push_back("--setenv");
521 options->push_back("CONNMAN_PATH");
522 options->push_back(rpc_task_->GetRpcIdentifier());
523 options->push_back("--script-security");
524 options->push_back("2");
525 options->push_back("--up");
526 options->push_back(kOpenVPNScript);
527 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100528
529 // Disable openvpn handling since we do route+ifconfig work.
530 options->push_back("--route-noexec");
531 options->push_back("--ifconfig-noexec");
532
533 // Drop root privileges on connection and enable callback scripts to send
534 // notify messages.
535 options->push_back("--user");
536 options->push_back("openvpn");
537 options->push_back("--group");
538 options->push_back("openvpn");
539}
540
Darin Petkove0d5dd12012-04-04 16:10:48 +0200541bool OpenVPNDriver::InitNSSOptions(vector<string> *options, Error *error) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200542 string ca_cert =
543 args()->LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200544 if (!ca_cert.empty()) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200545 if (!args()->LookupString(flimflam::kOpenVPNCaCertProperty, "").empty()) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200546 Error::PopulateAndLog(error,
547 Error::kInvalidArguments,
548 "Can't specify both CACert and CACertNSS.");
549 return false;
550 }
Darin Petkovb451d6e2012-04-23 11:56:41 +0200551 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200552 vector<char> id(vpnhost.begin(), vpnhost.end());
553 FilePath certfile = nss_->GetPEMCertfile(ca_cert, id);
554 if (certfile.empty()) {
555 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
556 } else {
557 options->push_back("--ca");
558 options->push_back(certfile.value());
559 }
560 }
561 return true;
562}
563
564void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200565 string id = args()->LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200566 if (!id.empty()) {
567 string provider =
Darin Petkovb451d6e2012-04-23 11:56:41 +0200568 args()->LookupString(flimflam::kOpenVPNProviderProperty, "");
Darin Petkove0d5dd12012-04-04 16:10:48 +0200569 if (provider.empty()) {
570 provider = kDefaultPKCS11Provider;
571 }
572 options->push_back("--pkcs11-providers");
573 options->push_back(provider);
574 options->push_back("--pkcs11-id");
575 options->push_back(id);
576 }
577}
578
579bool OpenVPNDriver::InitManagementChannelOptions(
580 vector<string> *options, Error *error) {
581 if (!management_server_->Start(dispatcher_, &sockets_, options)) {
582 Error::PopulateAndLog(
583 error, Error::kInternalError, "Unable to setup management channel.");
584 return false;
585 }
586 return true;
587}
588
Darin Petkov55771b72012-04-25 09:25:19 +0200589void OpenVPNDriver::InitLoggingOptions(vector<string> *options) {
590 options->push_back("--syslog");
591
592 string verb = args()->LookupString(kOpenVPNVerbProperty, "");
593 if (verb.empty() && SLOG_IS_ON(VPN, 0)) {
594 verb = "3";
595 }
596 if (!verb.empty()) {
597 options->push_back("--verb");
598 options->push_back(verb);
599 }
600}
601
Darin Petkov46463022012-03-29 14:57:32 +0200602bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100603 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200604 string value = args()->LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100605 if (!value.empty()) {
606 options->push_back(option);
607 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200608 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100609 }
Darin Petkov46463022012-03-29 14:57:32 +0200610 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100611}
612
Darin Petkov46463022012-03-29 14:57:32 +0200613bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100614 const string &property, const string &option, vector<string> *options) {
Darin Petkovb451d6e2012-04-23 11:56:41 +0200615 if (args()->ContainsString(property)) {
Darin Petkovfe6a9372012-02-28 16:25:06 +0100616 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200617 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100618 }
Darin Petkov46463022012-03-29 14:57:32 +0200619 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100620}
621
Darin Petkov6aa21872012-03-09 16:10:19 +0100622void OpenVPNDriver::Disconnect() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700623 SLOG(VPN, 2) << __func__;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100624 Cleanup(Service::kStateIdle);
Darin Petkov6aa21872012-03-09 16:10:19 +0100625}
626
Darin Petkov5eb05422012-05-11 15:45:25 +0200627void OpenVPNDriver::OnConnectionDisconnected() {
628 SLOG(VPN, 2) << __func__;
629 Cleanup(Service::kStateFailure);
630}
631
Darin Petkov271fe522012-03-27 13:47:29 +0200632void OpenVPNDriver::OnReconnecting() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700633 SLOG(VPN, 2) << __func__;
Darin Petkov271fe522012-03-27 13:47:29 +0200634 if (device_) {
635 device_->OnDisconnected();
636 }
637 if (service_) {
638 service_->SetState(Service::kStateAssociating);
639 }
640}
641
Paul Stewart39964fa2012-04-04 09:50:25 -0700642string OpenVPNDriver::GetProviderType() const {
643 return flimflam::kProviderOpenVpn;
644}
645
Darin Petkovb536a742012-04-26 11:31:28 +0200646KeyValueStore OpenVPNDriver::GetProvider(Error *error) {
647 SLOG(VPN, 2) << __func__;
648 KeyValueStore props = VPNDriver::GetProvider(error);
649 props.SetBool(flimflam::kPassphraseRequiredProperty,
650 args()->LookupString(
651 flimflam::kOpenVPNPasswordProperty, "").empty());
652 return props;
653}
654
Darin Petkov1a462de2012-05-02 11:10:48 +0200655// TODO(petkov): Consider refactoring lsb-release parsing out into a shared
656// singleton if it's used outside OpenVPN.
657bool OpenVPNDriver::ParseLSBRelease(map<string, string> *lsb_release) {
658 SLOG(VPN, 2) << __func__ << "(" << lsb_release_file_.value() << ")";
659 string contents;
660 if (!file_util::ReadFileToString(lsb_release_file_, &contents)) {
661 LOG(ERROR) << "Unable to read the lsb-release file: "
662 << lsb_release_file_.value();
663 return false;
664 }
665 vector<string> lines;
666 SplitString(contents, '\n', &lines);
667 for (vector<string>::const_iterator it = lines.begin();
668 it != lines.end(); ++it) {
669 size_t assign = it->find('=');
670 if (assign == string::npos) {
671 continue;
672 }
673 (*lsb_release)[it->substr(0, assign)] = it->substr(assign + 1);
674 }
675 return true;
676}
677
678void OpenVPNDriver::InitEnvironment(vector<string> *environment) {
679 // Adds the platform name and version to the environment so that openvpn can
680 // send them to the server when OpenVPN.PushPeerInfo is set.
681 map<string, string> lsb_release;
682 ParseLSBRelease(&lsb_release);
683 string platform_name = lsb_release[kChromeOSReleaseName];
684 if (!platform_name.empty()) {
685 environment->push_back("IV_PLAT=" + platform_name);
686 }
687 string platform_version = lsb_release[kChromeOSReleaseVersion];
688 if (!platform_version.empty()) {
689 environment->push_back("IV_PLAT_REL=" + platform_version);
690 }
691}
692
Darin Petkov33af05c2012-02-28 10:10:30 +0100693} // namespace shill