blob: 4b312350938b07808f3fd52cd45a1902671d5df6 [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"
Paul Stewartebd38562012-03-23 13:06:40 -070023#include "shill/property_accessor.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 Petkovf3c71d72012-03-21 12:32:15 +010026#include "shill/store_interface.h"
Darin Petkovf20994f2012-03-05 16:12:19 +010027#include "shill/vpn.h"
Darin Petkov79d74c92012-03-07 17:20:32 +010028#include "shill/vpn_service.h"
Darin Petkov33af05c2012-02-28 10:10:30 +010029
Darin Petkov78f63262012-03-26 01:30:24 +020030using base::SplitString;
Darin Petkov14c29ec2012-03-02 11:34:19 +010031using std::map;
Darin Petkovfe6a9372012-02-28 16:25:06 +010032using std::string;
33using std::vector;
34
Darin Petkov33af05c2012-02-28 10:10:30 +010035namespace shill {
36
Darin Petkova9b1fed2012-02-29 11:49:05 +010037namespace {
Darin Petkov14c29ec2012-03-02 11:34:19 +010038const char kOpenVPNForeignOptionPrefix[] = "foreign_option_";
39const char kOpenVPNIfconfigBroadcast[] = "ifconfig_broadcast";
40const char kOpenVPNIfconfigLocal[] = "ifconfig_local";
41const char kOpenVPNIfconfigNetmask[] = "ifconfig_netmask";
42const char kOpenVPNIfconfigRemote[] = "ifconfig_remote";
43const char kOpenVPNRouteOptionPrefix[] = "route_";
44const char kOpenVPNRouteVPNGateway[] = "route_vpn_gateway";
45const char kOpenVPNTrustedIP[] = "trusted_ip";
46const char kOpenVPNTunMTU[] = "tun_mtu";
Darin Petkovf3c71d72012-03-21 12:32:15 +010047
Darin Petkove0d5dd12012-04-04 16:10:48 +020048const char kDefaultPKCS11Provider[] = "libchaps.so";
49
Darin Petkovf3c71d72012-03-21 12:32:15 +010050// TODO(petkov): Move to chromeos/dbus/service_constants.h.
51const char kOpenVPNCertProperty[] = "OpenVPN.Cert";
52const char kOpenVPNKeyProperty[] = "OpenVPN.Key";
53const char kOpenVPNPingProperty[] = "OpenVPN.Ping";
54const char kOpenVPNPingExitProperty[] = "OpenVPN.PingExit";
55const char kOpenVPNPingRestartProperty[] = "OpenVPN.PingRestart";
56const char kOpenVPNTLSAuthProperty[] = "OpenVPN.TLSAuth";
57const char kOpenVPNVerbProperty[] = "OpenVPN.Verb";
58const char kVPNMTUProperty[] = "VPN.MTU";
Paul Stewartebd38562012-03-23 13:06:40 -070059} // namespace
60
61// static
62const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
63// static
64const char OpenVPNDriver::kOpenVPNScript[] = SCRIPTDIR "/openvpn-script";
65// static
66const OpenVPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkov1847d792012-04-17 11:33:06 +020067 { flimflam::kOpenVPNAuthNoCacheProperty, 0 },
68 { flimflam::kOpenVPNAuthProperty, 0 },
69 { flimflam::kOpenVPNAuthRetryProperty, 0 },
70 { flimflam::kOpenVPNAuthUserPassProperty, 0 },
71 { flimflam::kOpenVPNCaCertNSSProperty, 0 },
72 { flimflam::kOpenVPNCaCertProperty, 0 },
73 { flimflam::kOpenVPNCipherProperty, 0 },
74 { flimflam::kOpenVPNClientCertIdProperty,
75 OpenVPNDriver::Property::kEphemeral | OpenVPNDriver::Property::kCrypted },
76 { flimflam::kOpenVPNCompLZOProperty, 0 },
77 { flimflam::kOpenVPNCompNoAdaptProperty, 0 },
78 { flimflam::kOpenVPNKeyDirectionProperty, 0 },
79 { flimflam::kOpenVPNNsCertTypeProperty, 0 },
80 { flimflam::kOpenVPNOTPProperty,
81 OpenVPNDriver::Property::kEphemeral | OpenVPNDriver::Property::kCrypted },
82 { flimflam::kOpenVPNPasswordProperty, OpenVPNDriver::Property::kCrypted },
83 { flimflam::kOpenVPNPinProperty, 0 },
84 { flimflam::kOpenVPNPortProperty, 0 },
85 { flimflam::kOpenVPNProtoProperty, 0 },
86 { flimflam::kOpenVPNProviderProperty, 0 },
87 { flimflam::kOpenVPNPushPeerInfoProperty, 0 },
88 { flimflam::kOpenVPNRemoteCertEKUProperty, 0 },
89 { flimflam::kOpenVPNRemoteCertKUProperty, 0 },
90 { flimflam::kOpenVPNRemoteCertTLSProperty, 0 },
91 { flimflam::kOpenVPNRenegSecProperty, 0 },
92 { flimflam::kOpenVPNServerPollTimeoutProperty, 0 },
93 { flimflam::kOpenVPNShaperProperty, 0 },
94 { flimflam::kOpenVPNStaticChallengeProperty, 0 },
95 { flimflam::kOpenVPNTLSAuthContentsProperty, 0 },
96 { flimflam::kOpenVPNTLSRemoteProperty, 0 },
97 { flimflam::kOpenVPNUserProperty, 0 },
98 { flimflam::kProviderHostProperty, 0 },
99 { flimflam::kProviderNameProperty, 0 },
100 { flimflam::kProviderTypeProperty, 0 },
101 { kOpenVPNCertProperty, 0 },
102 { kOpenVPNKeyProperty, 0 },
103 { kOpenVPNPingExitProperty, 0 },
104 { kOpenVPNPingProperty, 0 },
105 { kOpenVPNPingRestartProperty, 0 },
106 { kOpenVPNTLSAuthProperty, 0 },
107 { kOpenVPNVerbProperty, 0 },
108 { kVPNMTUProperty, 0 },
Paul Stewart22807992012-04-11 08:48:31 -0700109
110 // Provided only for compatibility. crosbug.com/29286
Darin Petkov1847d792012-04-17 11:33:06 +0200111 { flimflam::kOpenVPNMgmtEnableProperty, 0 },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100112};
Paul Stewart291a4732012-03-14 19:19:02 -0700113
Darin Petkova9b1fed2012-02-29 11:49:05 +0100114OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100115 EventDispatcher *dispatcher,
116 Metrics *metrics,
117 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800118 DeviceInfo *device_info,
Paul Stewart451aa7f2012-04-11 19:07:58 -0700119 GLib *glib)
Darin Petkova9b1fed2012-02-29 11:49:05 +0100120 : control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100121 dispatcher_(dispatcher),
122 metrics_(metrics),
123 manager_(manager),
Paul Stewartca6abd42012-03-01 15:45:29 -0800124 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100125 glib_(glib),
Darin Petkov46463022012-03-29 14:57:32 +0200126 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200127 nss_(NSS::GetInstance()),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100128 pid_(0),
129 child_watch_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100130
Darin Petkov36a3ace2012-03-06 17:22:14 +0100131OpenVPNDriver::~OpenVPNDriver() {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100132 Cleanup(Service::kStateIdle);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100133}
134
Darin Petkov3f9131c2012-03-20 11:37:32 +0100135void OpenVPNDriver::Cleanup(Service::ConnectState state) {
Darin Petkov1fa81942012-04-02 11:38:08 +0200136 VLOG(2) << __func__ << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov46463022012-03-29 14:57:32 +0200137 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200138 if (!tls_auth_file_.empty()) {
139 file_util::Delete(tls_auth_file_, false);
140 tls_auth_file_.clear();
141 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100142 if (child_watch_tag_) {
143 glib_->SourceRemove(child_watch_tag_);
144 child_watch_tag_ = 0;
145 CHECK(pid_);
146 kill(pid_, SIGTERM);
147 }
148 if (pid_) {
149 glib_->SpawnClosePID(pid_);
150 pid_ = 0;
151 }
152 rpc_task_.reset();
153 if (device_) {
154 int interface_index = device_->interface_index();
Darin Petkov029d3532012-04-18 14:38:04 +0200155 device_->OnDisconnected();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500156 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100157 device_ = NULL;
158 device_info_->DeleteInterface(interface_index);
159 }
160 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100161 if (service_) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100162 service_->SetState(state);
Darin Petkov79d74c92012-03-07 17:20:32 +0100163 service_ = NULL;
164 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100165}
166
167bool OpenVPNDriver::SpawnOpenVPN() {
168 VLOG(2) << __func__ << "(" << tunnel_interface_ << ")";
169
170 vector<string> options;
171 Error error;
172 InitOptions(&options, &error);
173 if (error.IsFailure()) {
174 return false;
175 }
176 VLOG(2) << "OpenVPN process options: " << JoinString(options, ' ');
177
178 // TODO(petkov): This code needs to be abstracted away in a separate external
179 // process module (crosbug.com/27131).
180 vector<char *> process_args;
181 process_args.push_back(const_cast<char *>(kOpenVPNPath));
182 for (vector<string>::const_iterator it = options.begin();
183 it != options.end(); ++it) {
184 process_args.push_back(const_cast<char *>(it->c_str()));
185 }
186 process_args.push_back(NULL);
187 char *envp[1] = { NULL };
188
189 CHECK(!pid_);
190 // Redirect all openvpn output to stderr.
191 int stderr_fd = fileno(stderr);
192 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
193 envp,
194 G_SPAWN_DO_NOT_REAP_CHILD,
195 NULL,
196 NULL,
197 &pid_,
198 NULL,
199 &stderr_fd,
200 &stderr_fd,
201 NULL)) {
202 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
203 return false;
204 }
205 CHECK(!child_watch_tag_);
206 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
207 return true;
208}
209
210// static
211void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
212 VLOG(2) << __func__ << "(" << pid << ", " << status << ")";
213 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
214 me->child_watch_tag_ = 0;
215 CHECK_EQ(pid, me->pid_);
Darin Petkov3f9131c2012-03-20 11:37:32 +0100216 me->Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100217 // TODO(petkov): Figure if we need to restart the connection.
218}
Darin Petkov33af05c2012-02-28 10:10:30 +0100219
Paul Stewartca6abd42012-03-01 15:45:29 -0800220bool OpenVPNDriver::ClaimInterface(const string &link_name,
221 int interface_index) {
222 if (link_name != tunnel_interface_) {
223 return false;
224 }
225
226 VLOG(2) << "Claiming " << link_name << " for OpenVPN tunnel";
227
Darin Petkovf20994f2012-03-05 16:12:19 +0100228 CHECK(!device_);
229 device_ = new VPN(control_, dispatcher_, metrics_, manager_,
230 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500231
232 device_->SetEnabled(true);
Darin Petkov79d74c92012-03-07 17:20:32 +0100233 device_->SelectService(service_);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500234
Darin Petkov36a3ace2012-03-06 17:22:14 +0100235 rpc_task_.reset(new RPCTask(control_, this));
236 if (!SpawnOpenVPN()) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100237 Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100238 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800239 return true;
240}
241
Darin Petkov36a3ace2012-03-06 17:22:14 +0100242void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100243 const map<string, string> &dict) {
244 VLOG(2) << __func__ << "(" << reason << ")";
245 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100246 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100247 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100248 }
249 IPConfig::Properties properties;
250 ParseIPConfiguration(dict, &properties);
Paul Stewartce4ec192012-03-14 12:53:46 -0700251 PinHostRoute(properties);
252
Darin Petkov79d74c92012-03-07 17:20:32 +0100253 device_->UpdateIPConfig(properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100254}
255
256// static
257void OpenVPNDriver::ParseIPConfiguration(
258 const map<string, string> &configuration,
259 IPConfig::Properties *properties) {
260 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100261 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100262 properties->address_family = IPAddress::kFamilyIPv4;
Paul Stewart48100b02012-03-19 07:53:52 -0700263 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
264 properties->address_family);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100265 for (map<string, string>::const_iterator it = configuration.begin();
266 it != configuration.end(); ++it) {
267 const string &key = it->first;
268 const string &value = it->second;
269 VLOG(2) << "Processing: " << key << " -> " << value;
270 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
271 properties->address = value;
272 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
273 properties->broadcast_address = value;
274 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700275 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100276 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
277 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
278 properties->peer_address = value;
279 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
280 properties->gateway = value;
281 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700282 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100283 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
284 int mtu = 0;
285 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
286 properties->mtu = mtu;
287 } else {
288 LOG(ERROR) << "MTU " << value << " ignored.";
289 }
290 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
291 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
292 int order = 0;
293 if (base::StringToInt(suffix, &order)) {
294 foreign_options[order] = value;
295 } else {
296 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
297 }
298 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100299 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
300 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100301 } else {
302 VLOG(2) << "Key ignored.";
303 }
304 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100305 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100306 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100307}
308
309// static
310void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
311 IPConfig::Properties *properties) {
312 for (ForeignOptions::const_iterator it = options.begin();
313 it != options.end(); ++it) {
314 ParseForeignOption(it->second, properties);
315 }
316}
317
318// static
319void OpenVPNDriver::ParseForeignOption(const string &option,
320 IPConfig::Properties *properties) {
321 VLOG(2) << __func__ << "(" << option << ")";
322 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200323 SplitString(option, ' ', &tokens);
324 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100325 return;
326 }
327 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
328 properties->domain_search.push_back(tokens[2]);
329 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
330 properties->dns_servers.push_back(tokens[2]);
331 }
332}
333
Darin Petkov60596742012-03-05 12:17:17 +0100334// static
335IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
336 const string &prefix, const string &key, RouteOptions *routes) {
337 int order = 0;
338 if (!StartsWithASCII(key, prefix, false) ||
339 !base::StringToInt(key.substr(prefix.size()), &order)) {
340 return NULL;
341 }
342 return &(*routes)[order];
343}
344
345// static
346void OpenVPNDriver::ParseRouteOption(
347 const string &key, const string &value, RouteOptions *routes) {
348 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
349 if (route) {
350 route->host = value;
351 return;
352 }
353 route = GetRouteOptionEntry("netmask_", key, routes);
354 if (route) {
355 route->netmask = value;
356 return;
357 }
358 route = GetRouteOptionEntry("gateway_", key, routes);
359 if (route) {
360 route->gateway = value;
361 return;
362 }
363 LOG(WARNING) << "Unknown route option ignored: " << key;
364}
365
366// static
367void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
368 IPConfig::Properties *properties) {
369 for (RouteOptions::const_iterator it = routes.begin();
370 it != routes.end(); ++it) {
371 const IPConfig::Route &route = it->second;
372 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
373 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
374 continue;
375 }
376 properties->routes.push_back(route);
377 }
378}
379
Darin Petkov79d74c92012-03-07 17:20:32 +0100380void OpenVPNDriver::Connect(const VPNServiceRefPtr &service,
381 Error *error) {
382 service_ = service;
383 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100384 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
385 Error::PopulateAndLog(
386 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov3f9131c2012-03-20 11:37:32 +0100387 Cleanup(Service::kStateFailure);
Darin Petkovf20994f2012-03-05 16:12:19 +0100388 }
389 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100390}
391
Darin Petkovfe6a9372012-02-28 16:25:06 +0100392void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkov7f060332012-03-14 11:46:47 +0100393 string vpnhost = args_.LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100394 if (vpnhost.empty()) {
395 Error::PopulateAndLog(
396 error, Error::kInvalidArguments, "VPN host not specified.");
397 return;
398 }
399 options->push_back("--client");
400 options->push_back("--tls-client");
401 options->push_back("--remote");
402 options->push_back(vpnhost);
403 options->push_back("--nobind");
404 options->push_back("--persist-key");
405 options->push_back("--persist-tun");
406
Darin Petkovf20994f2012-03-05 16:12:19 +0100407 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800408 options->push_back("--dev");
409 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100410 options->push_back("--dev-type");
411 options->push_back("tun");
412 options->push_back("--syslog");
413
414 // TODO(petkov): Enable verbosity based on shill logging options too.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100415 AppendValueOption(kOpenVPNVerbProperty, "--verb", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100416
Darin Petkovf3c71d72012-03-21 12:32:15 +0100417 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100418 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
419 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100420 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200421 {
422 string contents =
423 args_.LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
424 if (!contents.empty()) {
425 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
426 file_util::WriteFile(
427 tls_auth_file_, contents.data(), contents.size()) !=
428 static_cast<int>(contents.size())) {
429 Error::PopulateAndLog(
430 error, Error::kInternalError, "Unable to setup tls-auth file.");
431 return;
432 }
433 options->push_back("--tls-auth");
434 options->push_back(tls_auth_file_.value());
435 }
436 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100437 AppendValueOption(
438 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
439 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
440 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
441 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
442 AppendValueOption(
443 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
444 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
445 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
446 AppendFlag(
447 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
448 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
449 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
450 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
451 "--server-poll-timeout", options);
452
Darin Petkove0d5dd12012-04-04 16:10:48 +0200453 if (!InitNSSOptions(options, error)) {
454 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200455 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100456
457 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100458 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
459 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
460 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100461
462 AppendValueOption(flimflam::kOpenVPNCaCertProperty, "--ca", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100463 AppendValueOption(kOpenVPNCertProperty, "--cert", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100464 AppendValueOption(
465 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100466 AppendValueOption(kOpenVPNKeyProperty, "--key", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100467
Darin Petkove0d5dd12012-04-04 16:10:48 +0200468 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100469
470 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100471 string remote_cert_tls =
472 args_.LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100473 if (remote_cert_tls.empty()) {
474 remote_cert_tls = "server";
475 }
476 if (remote_cert_tls != "none") {
477 options->push_back("--remote-cert-tls");
478 options->push_back(remote_cert_tls);
479 }
480
481 // This is an undocumented command line argument that works like a .cfg file
482 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
483 AppendValueOption(
484 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
485 // TODO(sleffler): Support more than one eku parameter.
486 AppendValueOption(
487 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
488 AppendValueOption(
489 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
490
Darin Petkove0d5dd12012-04-04 16:10:48 +0200491 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200492 return;
493 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100494
Darin Petkova9b1fed2012-02-29 11:49:05 +0100495 // Setup openvpn-script options and RPC information required to send back
496 // Layer 3 configuration.
497 options->push_back("--setenv");
498 options->push_back("CONNMAN_BUSNAME");
499 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
500 options->push_back("--setenv");
501 options->push_back("CONNMAN_INTERFACE");
502 options->push_back(rpc_task_->GetRpcInterfaceIdentifier());
503 options->push_back("--setenv");
504 options->push_back("CONNMAN_PATH");
505 options->push_back(rpc_task_->GetRpcIdentifier());
506 options->push_back("--script-security");
507 options->push_back("2");
508 options->push_back("--up");
509 options->push_back(kOpenVPNScript);
510 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100511
512 // Disable openvpn handling since we do route+ifconfig work.
513 options->push_back("--route-noexec");
514 options->push_back("--ifconfig-noexec");
515
516 // Drop root privileges on connection and enable callback scripts to send
517 // notify messages.
518 options->push_back("--user");
519 options->push_back("openvpn");
520 options->push_back("--group");
521 options->push_back("openvpn");
522}
523
Darin Petkove0d5dd12012-04-04 16:10:48 +0200524bool OpenVPNDriver::InitNSSOptions(vector<string> *options, Error *error) {
525 string ca_cert = args_.LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
526 if (!ca_cert.empty()) {
527 if (!args_.LookupString(flimflam::kOpenVPNCaCertProperty, "").empty()) {
528 Error::PopulateAndLog(error,
529 Error::kInvalidArguments,
530 "Can't specify both CACert and CACertNSS.");
531 return false;
532 }
533 const string &vpnhost = args_.GetString(flimflam::kProviderHostProperty);
534 vector<char> id(vpnhost.begin(), vpnhost.end());
535 FilePath certfile = nss_->GetPEMCertfile(ca_cert, id);
536 if (certfile.empty()) {
537 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
538 } else {
539 options->push_back("--ca");
540 options->push_back(certfile.value());
541 }
542 }
543 return true;
544}
545
546void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
547 string id = args_.LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
548 if (!id.empty()) {
549 string provider =
550 args_.LookupString(flimflam::kOpenVPNProviderProperty, "");
551 if (provider.empty()) {
552 provider = kDefaultPKCS11Provider;
553 }
554 options->push_back("--pkcs11-providers");
555 options->push_back(provider);
556 options->push_back("--pkcs11-id");
557 options->push_back(id);
558 }
559}
560
561bool OpenVPNDriver::InitManagementChannelOptions(
562 vector<string> *options, Error *error) {
563 if (!management_server_->Start(dispatcher_, &sockets_, options)) {
564 Error::PopulateAndLog(
565 error, Error::kInternalError, "Unable to setup management channel.");
566 return false;
567 }
568 return true;
569}
570
Darin Petkov46463022012-03-29 14:57:32 +0200571bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100572 const string &property, const string &option, vector<string> *options) {
Darin Petkov7f060332012-03-14 11:46:47 +0100573 string value = args_.LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100574 if (!value.empty()) {
575 options->push_back(option);
576 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200577 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100578 }
Darin Petkov46463022012-03-29 14:57:32 +0200579 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100580}
581
Darin Petkov46463022012-03-29 14:57:32 +0200582bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100583 const string &property, const string &option, vector<string> *options) {
584 if (args_.ContainsString(property)) {
585 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200586 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100587 }
Darin Petkov46463022012-03-29 14:57:32 +0200588 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100589}
590
Paul Stewartce4ec192012-03-14 12:53:46 -0700591bool OpenVPNDriver::PinHostRoute(const IPConfig::Properties &properties) {
592 if (properties.gateway.empty() || properties.trusted_ip.empty()) {
593 return false;
594 }
595
596 IPAddress trusted_ip(properties.address_family);
597 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
598 LOG(ERROR) << "Failed to parse trusted_ip "
599 << properties.trusted_ip << "; ignored.";
600 return false;
601 }
602
603 ServiceRefPtr default_service = manager_->GetDefaultService();
604 if (!default_service) {
605 LOG(ERROR) << "No default service exists.";
606 return false;
607 }
608
609 CHECK(default_service->connection());
610 return default_service->connection()->RequestHostRoute(trusted_ip);
611}
612
Darin Petkov6aa21872012-03-09 16:10:19 +0100613void OpenVPNDriver::Disconnect() {
Darin Petkov271fe522012-03-27 13:47:29 +0200614 VLOG(2) << __func__;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100615 Cleanup(Service::kStateIdle);
Darin Petkov6aa21872012-03-09 16:10:19 +0100616}
617
Darin Petkov271fe522012-03-27 13:47:29 +0200618void OpenVPNDriver::OnReconnecting() {
619 VLOG(2) << __func__;
620 if (device_) {
621 device_->OnDisconnected();
622 }
623 if (service_) {
624 service_->SetState(Service::kStateAssociating);
625 }
626}
627
Darin Petkovf3c71d72012-03-21 12:32:15 +0100628bool OpenVPNDriver::Load(StoreInterface *storage, const string &storage_id) {
Paul Stewartebd38562012-03-23 13:06:40 -0700629 for (size_t i = 0; i < arraysize(kProperties); i++) {
Darin Petkov1847d792012-04-17 11:33:06 +0200630 if ((kProperties[i].flags & Property::kEphemeral)) {
631 continue;
632 }
Darin Petkovf3c71d72012-03-21 12:32:15 +0100633 const string property = kProperties[i].property;
634 string value;
Darin Petkov1847d792012-04-17 11:33:06 +0200635 bool loaded = (kProperties[i].flags & Property::kCrypted) ?
Darin Petkovf3c71d72012-03-21 12:32:15 +0100636 storage->GetCryptedString(storage_id, property, &value) :
637 storage->GetString(storage_id, property, &value);
638 if (loaded) {
639 args_.SetString(property, value);
Paul Stewart141983b2012-03-23 07:58:32 -0700640 } else {
641 args_.RemoveString(property);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100642 }
643 }
644 return true;
645}
646
647bool OpenVPNDriver::Save(StoreInterface *storage, const string &storage_id) {
Paul Stewartebd38562012-03-23 13:06:40 -0700648 for (size_t i = 0; i < arraysize(kProperties); i++) {
Darin Petkov1847d792012-04-17 11:33:06 +0200649 if ((kProperties[i].flags & Property::kEphemeral)) {
650 continue;
651 }
Darin Petkovf3c71d72012-03-21 12:32:15 +0100652 const string property = kProperties[i].property;
653 const string value = args_.LookupString(property, "");
654 if (value.empty()) {
655 storage->DeleteKey(storage_id, property);
Darin Petkov1847d792012-04-17 11:33:06 +0200656 } else if ((kProperties[i].flags & Property::kCrypted)) {
Darin Petkovf3c71d72012-03-21 12:32:15 +0100657 storage->SetCryptedString(storage_id, property, value);
658 } else {
659 storage->SetString(storage_id, property, value);
660 }
661 }
662 return true;
663}
664
Paul Stewartebd38562012-03-23 13:06:40 -0700665void OpenVPNDriver::InitPropertyStore(PropertyStore *store) {
666 for (size_t i = 0; i < arraysize(kProperties); i++) {
667 store->RegisterDerivedString(
668 kProperties[i].property,
669 StringAccessor(
670 new CustomMappedAccessor<OpenVPNDriver, string, size_t>(
671 this,
672 &OpenVPNDriver::ClearMappedProperty,
673 &OpenVPNDriver::GetMappedProperty,
674 &OpenVPNDriver::SetMappedProperty,
675 i)));
676 }
677
Paul Stewart451aa7f2012-04-11 19:07:58 -0700678 store->RegisterDerivedStringmap(
679 flimflam::kProviderProperty,
680 StringmapAccessor(
681 new CustomAccessor<OpenVPNDriver, Stringmap>(
682 this, &OpenVPNDriver::GetProvider, NULL)));
683
Paul Stewartebd38562012-03-23 13:06:40 -0700684 // TODO(pstew): Add the PassphraseRequired and PSKRequired properties.
685 // crosbug.com/27323
686}
687
Paul Stewart39964fa2012-04-04 09:50:25 -0700688string OpenVPNDriver::GetProviderType() const {
689 return flimflam::kProviderOpenVpn;
690}
691
Paul Stewartebd38562012-03-23 13:06:40 -0700692void OpenVPNDriver::ClearMappedProperty(const size_t &index,
693 Error *error) {
694 CHECK(index < arraysize(kProperties));
695 if (args_.ContainsString(kProperties[index].property)) {
696 args_.RemoveString(kProperties[index].property);
697 } else {
698 error->Populate(Error::kNotFound, "Property is not set");
699 }
700}
701
702string OpenVPNDriver::GetMappedProperty(const size_t &index,
703 Error *error) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700704 // Provider properties are set via SetProperty calls to "Provider.XXX",
705 // however, they are retrieved via a GetProperty call, which returns
706 // all properties in a single "Provider" dict. Therefore, none of
707 // the individual properties in the kProperties are available for
708 // enumeration in GetProperties. Instead, they are retrieved via
709 // GetProvider below.
710 error->Populate(Error::kInvalidArguments,
711 "Provider properties are not read back in this manner");
712 return string();
Paul Stewartebd38562012-03-23 13:06:40 -0700713}
714
715void OpenVPNDriver::SetMappedProperty(const size_t &index,
716 const string &value,
717 Error *error) {
718 CHECK(index < arraysize(kProperties));
719 args_.SetString(kProperties[index].property, value);
720}
721
Paul Stewart451aa7f2012-04-11 19:07:58 -0700722Stringmap OpenVPNDriver::GetProvider(Error *error) {
723 string provider_prefix = string(flimflam::kProviderProperty) + ".";
724 Stringmap provider_properties;
725
726 for (size_t i = 0; i < arraysize(kProperties); i++) {
727 // Never return any encrypted properties.
Darin Petkov1847d792012-04-17 11:33:06 +0200728 if ((kProperties[i].flags & Property::kCrypted)) {
Paul Stewart451aa7f2012-04-11 19:07:58 -0700729 continue;
730 }
731
732 string prop = kProperties[i].property;
733
734 // Chomp off leading "Provider." from properties that have this prefix.
735 if (StartsWithASCII(prop, provider_prefix, false)) {
736 prop = prop.substr(provider_prefix.length());
737 }
738
739 if (args_.ContainsString(kProperties[i].property)) {
740 provider_properties[prop] =
741 args_.LookupString(kProperties[i].property, "");
742 }
743 }
744
745 return provider_properties;
746}
Paul Stewartebd38562012-03-23 13:06:40 -0700747
Darin Petkov33af05c2012-02-28 10:10:30 +0100748} // namespace shill