blob: be68cfae810d09c58369a383ef753931f35d992a [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";
59
Paul Stewartebd38562012-03-23 13:06:40 -070060} // namespace
61
62// static
63const char OpenVPNDriver::kOpenVPNPath[] = "/usr/sbin/openvpn";
64// static
65const char OpenVPNDriver::kOpenVPNScript[] = SCRIPTDIR "/openvpn-script";
66// static
67const OpenVPNDriver::Property OpenVPNDriver::kProperties[] = {
Darin Petkovf3c71d72012-03-21 12:32:15 +010068 { flimflam::kOpenVPNAuthNoCacheProperty, false },
69 { flimflam::kOpenVPNAuthProperty, false },
70 { flimflam::kOpenVPNAuthRetryProperty, false },
71 { flimflam::kOpenVPNAuthUserPassProperty, false },
72 { flimflam::kOpenVPNCaCertNSSProperty, false },
73 { flimflam::kOpenVPNCaCertProperty, false },
74 { flimflam::kOpenVPNCipherProperty, false },
75 { flimflam::kOpenVPNCompLZOProperty, false },
76 { flimflam::kOpenVPNCompNoAdaptProperty, false },
77 { flimflam::kOpenVPNKeyDirectionProperty, false },
78 { flimflam::kOpenVPNNsCertTypeProperty, false },
79 { flimflam::kOpenVPNPasswordProperty, true },
80 { flimflam::kOpenVPNPinProperty, false },
81 { flimflam::kOpenVPNPortProperty, false },
82 { flimflam::kOpenVPNProtoProperty, false },
83 { flimflam::kOpenVPNProviderProperty, false },
84 { flimflam::kOpenVPNPushPeerInfoProperty, false },
85 { flimflam::kOpenVPNRemoteCertEKUProperty, false },
86 { flimflam::kOpenVPNRemoteCertKUProperty, false },
87 { flimflam::kOpenVPNRemoteCertTLSProperty, false },
88 { flimflam::kOpenVPNRenegSecProperty, false },
89 { flimflam::kOpenVPNServerPollTimeoutProperty, false },
90 { flimflam::kOpenVPNShaperProperty, false },
91 { flimflam::kOpenVPNStaticChallengeProperty, false },
92 { flimflam::kOpenVPNTLSAuthContentsProperty, false },
93 { flimflam::kOpenVPNTLSRemoteProperty, false },
94 { flimflam::kOpenVPNUserProperty, false },
95 { flimflam::kProviderHostProperty, false },
96 { flimflam::kProviderNameProperty, false },
97 { flimflam::kProviderTypeProperty, false },
98 { kOpenVPNCertProperty, false },
99 { kOpenVPNKeyProperty, false },
100 { kOpenVPNPingExitProperty, false },
101 { kOpenVPNPingProperty, false },
102 { kOpenVPNPingRestartProperty, false },
103 { kOpenVPNTLSAuthProperty, false },
104 { kOpenVPNVerbProperty, false },
105 { kVPNMTUProperty, false },
Darin Petkovf3c71d72012-03-21 12:32:15 +0100106};
Paul Stewart291a4732012-03-14 19:19:02 -0700107
Darin Petkova9b1fed2012-02-29 11:49:05 +0100108OpenVPNDriver::OpenVPNDriver(ControlInterface *control,
Darin Petkovf20994f2012-03-05 16:12:19 +0100109 EventDispatcher *dispatcher,
110 Metrics *metrics,
111 Manager *manager,
Paul Stewartca6abd42012-03-01 15:45:29 -0800112 DeviceInfo *device_info,
Darin Petkov36a3ace2012-03-06 17:22:14 +0100113 GLib *glib,
Darin Petkova9b1fed2012-02-29 11:49:05 +0100114 const KeyValueStore &args)
115 : control_(control),
Darin Petkovf20994f2012-03-05 16:12:19 +0100116 dispatcher_(dispatcher),
117 metrics_(metrics),
118 manager_(manager),
Paul Stewartca6abd42012-03-01 15:45:29 -0800119 device_info_(device_info),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100120 glib_(glib),
121 args_(args),
Darin Petkov46463022012-03-29 14:57:32 +0200122 management_server_(new OpenVPNManagementServer(this, glib)),
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200123 nss_(NSS::GetInstance()),
Darin Petkov36a3ace2012-03-06 17:22:14 +0100124 pid_(0),
125 child_watch_tag_(0) {}
Darin Petkov33af05c2012-02-28 10:10:30 +0100126
Darin Petkov36a3ace2012-03-06 17:22:14 +0100127OpenVPNDriver::~OpenVPNDriver() {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100128 Cleanup(Service::kStateIdle);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100129}
130
Darin Petkov3f9131c2012-03-20 11:37:32 +0100131void OpenVPNDriver::Cleanup(Service::ConnectState state) {
Darin Petkov1fa81942012-04-02 11:38:08 +0200132 VLOG(2) << __func__ << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov46463022012-03-29 14:57:32 +0200133 management_server_->Stop();
Darin Petkov1fa81942012-04-02 11:38:08 +0200134 if (!tls_auth_file_.empty()) {
135 file_util::Delete(tls_auth_file_, false);
136 tls_auth_file_.clear();
137 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100138 if (child_watch_tag_) {
139 glib_->SourceRemove(child_watch_tag_);
140 child_watch_tag_ = 0;
141 CHECK(pid_);
142 kill(pid_, SIGTERM);
143 }
144 if (pid_) {
145 glib_->SpawnClosePID(pid_);
146 pid_ = 0;
147 }
148 rpc_task_.reset();
149 if (device_) {
150 int interface_index = device_->interface_index();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500151 device_->SetEnabled(false);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100152 device_ = NULL;
153 device_info_->DeleteInterface(interface_index);
154 }
155 tunnel_interface_.clear();
Darin Petkov79d74c92012-03-07 17:20:32 +0100156 if (service_) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100157 service_->SetState(state);
Darin Petkov79d74c92012-03-07 17:20:32 +0100158 service_ = NULL;
159 }
Darin Petkov36a3ace2012-03-06 17:22:14 +0100160}
161
162bool OpenVPNDriver::SpawnOpenVPN() {
163 VLOG(2) << __func__ << "(" << tunnel_interface_ << ")";
164
165 vector<string> options;
166 Error error;
167 InitOptions(&options, &error);
168 if (error.IsFailure()) {
169 return false;
170 }
171 VLOG(2) << "OpenVPN process options: " << JoinString(options, ' ');
172
173 // TODO(petkov): This code needs to be abstracted away in a separate external
174 // process module (crosbug.com/27131).
175 vector<char *> process_args;
176 process_args.push_back(const_cast<char *>(kOpenVPNPath));
177 for (vector<string>::const_iterator it = options.begin();
178 it != options.end(); ++it) {
179 process_args.push_back(const_cast<char *>(it->c_str()));
180 }
181 process_args.push_back(NULL);
182 char *envp[1] = { NULL };
183
184 CHECK(!pid_);
185 // Redirect all openvpn output to stderr.
186 int stderr_fd = fileno(stderr);
187 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
188 envp,
189 G_SPAWN_DO_NOT_REAP_CHILD,
190 NULL,
191 NULL,
192 &pid_,
193 NULL,
194 &stderr_fd,
195 &stderr_fd,
196 NULL)) {
197 LOG(ERROR) << "Unable to spawn: " << kOpenVPNPath;
198 return false;
199 }
200 CHECK(!child_watch_tag_);
201 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnOpenVPNDied, this);
202 return true;
203}
204
205// static
206void OpenVPNDriver::OnOpenVPNDied(GPid pid, gint status, gpointer data) {
207 VLOG(2) << __func__ << "(" << pid << ", " << status << ")";
208 OpenVPNDriver *me = reinterpret_cast<OpenVPNDriver *>(data);
209 me->child_watch_tag_ = 0;
210 CHECK_EQ(pid, me->pid_);
Darin Petkov3f9131c2012-03-20 11:37:32 +0100211 me->Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100212 // TODO(petkov): Figure if we need to restart the connection.
213}
Darin Petkov33af05c2012-02-28 10:10:30 +0100214
Paul Stewartca6abd42012-03-01 15:45:29 -0800215bool OpenVPNDriver::ClaimInterface(const string &link_name,
216 int interface_index) {
217 if (link_name != tunnel_interface_) {
218 return false;
219 }
220
221 VLOG(2) << "Claiming " << link_name << " for OpenVPN tunnel";
222
Darin Petkovf20994f2012-03-05 16:12:19 +0100223 CHECK(!device_);
224 device_ = new VPN(control_, dispatcher_, metrics_, manager_,
225 link_name, interface_index);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500226
227 device_->SetEnabled(true);
Darin Petkov79d74c92012-03-07 17:20:32 +0100228 device_->SelectService(service_);
Eric Shienbrood9a245532012-03-07 14:20:39 -0500229
Darin Petkov36a3ace2012-03-06 17:22:14 +0100230 rpc_task_.reset(new RPCTask(control_, this));
231 if (!SpawnOpenVPN()) {
Darin Petkov3f9131c2012-03-20 11:37:32 +0100232 Cleanup(Service::kStateFailure);
Darin Petkov36a3ace2012-03-06 17:22:14 +0100233 }
Paul Stewartca6abd42012-03-01 15:45:29 -0800234 return true;
235}
236
Darin Petkov36a3ace2012-03-06 17:22:14 +0100237void OpenVPNDriver::Notify(const string &reason,
Darin Petkov14c29ec2012-03-02 11:34:19 +0100238 const map<string, string> &dict) {
239 VLOG(2) << __func__ << "(" << reason << ")";
240 if (reason != "up") {
Darin Petkov79d74c92012-03-07 17:20:32 +0100241 device_->OnDisconnected();
Darin Petkov36a3ace2012-03-06 17:22:14 +0100242 return;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100243 }
244 IPConfig::Properties properties;
245 ParseIPConfiguration(dict, &properties);
Paul Stewartce4ec192012-03-14 12:53:46 -0700246 PinHostRoute(properties);
247
Darin Petkov79d74c92012-03-07 17:20:32 +0100248 device_->UpdateIPConfig(properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100249}
250
251// static
252void OpenVPNDriver::ParseIPConfiguration(
253 const map<string, string> &configuration,
254 IPConfig::Properties *properties) {
255 ForeignOptions foreign_options;
Darin Petkov60596742012-03-05 12:17:17 +0100256 RouteOptions routes;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100257 properties->address_family = IPAddress::kFamilyIPv4;
Paul Stewart48100b02012-03-19 07:53:52 -0700258 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
259 properties->address_family);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100260 for (map<string, string>::const_iterator it = configuration.begin();
261 it != configuration.end(); ++it) {
262 const string &key = it->first;
263 const string &value = it->second;
264 VLOG(2) << "Processing: " << key << " -> " << value;
265 if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigLocal)) {
266 properties->address = value;
267 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigBroadcast)) {
268 properties->broadcast_address = value;
269 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigNetmask)) {
Paul Stewart48100b02012-03-19 07:53:52 -0700270 properties->subnet_prefix =
Darin Petkov14c29ec2012-03-02 11:34:19 +0100271 IPAddress::GetPrefixLengthFromMask(properties->address_family, value);
272 } else if (LowerCaseEqualsASCII(key, kOpenVPNIfconfigRemote)) {
273 properties->peer_address = value;
274 } else if (LowerCaseEqualsASCII(key, kOpenVPNRouteVPNGateway)) {
275 properties->gateway = value;
276 } else if (LowerCaseEqualsASCII(key, kOpenVPNTrustedIP)) {
Paul Stewartce4ec192012-03-14 12:53:46 -0700277 properties->trusted_ip = value;
Darin Petkov14c29ec2012-03-02 11:34:19 +0100278 } else if (LowerCaseEqualsASCII(key, kOpenVPNTunMTU)) {
279 int mtu = 0;
280 if (base::StringToInt(value, &mtu) && mtu >= DHCPConfig::kMinMTU) {
281 properties->mtu = mtu;
282 } else {
283 LOG(ERROR) << "MTU " << value << " ignored.";
284 }
285 } else if (StartsWithASCII(key, kOpenVPNForeignOptionPrefix, false)) {
286 const string suffix = key.substr(strlen(kOpenVPNForeignOptionPrefix));
287 int order = 0;
288 if (base::StringToInt(suffix, &order)) {
289 foreign_options[order] = value;
290 } else {
291 LOG(ERROR) << "Ignored unexpected foreign option suffix: " << suffix;
292 }
293 } else if (StartsWithASCII(key, kOpenVPNRouteOptionPrefix, false)) {
Darin Petkov60596742012-03-05 12:17:17 +0100294 ParseRouteOption(key.substr(strlen(kOpenVPNRouteOptionPrefix)),
295 value, &routes);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100296 } else {
297 VLOG(2) << "Key ignored.";
298 }
299 }
Darin Petkov14c29ec2012-03-02 11:34:19 +0100300 ParseForeignOptions(foreign_options, properties);
Darin Petkov60596742012-03-05 12:17:17 +0100301 SetRoutes(routes, properties);
Darin Petkov14c29ec2012-03-02 11:34:19 +0100302}
303
304// static
305void OpenVPNDriver::ParseForeignOptions(const ForeignOptions &options,
306 IPConfig::Properties *properties) {
307 for (ForeignOptions::const_iterator it = options.begin();
308 it != options.end(); ++it) {
309 ParseForeignOption(it->second, properties);
310 }
311}
312
313// static
314void OpenVPNDriver::ParseForeignOption(const string &option,
315 IPConfig::Properties *properties) {
316 VLOG(2) << __func__ << "(" << option << ")";
317 vector<string> tokens;
Darin Petkov78f63262012-03-26 01:30:24 +0200318 SplitString(option, ' ', &tokens);
319 if (tokens.size() != 3 || !LowerCaseEqualsASCII(tokens[0], "dhcp-option")) {
Darin Petkov14c29ec2012-03-02 11:34:19 +0100320 return;
321 }
322 if (LowerCaseEqualsASCII(tokens[1], "domain")) {
323 properties->domain_search.push_back(tokens[2]);
324 } else if (LowerCaseEqualsASCII(tokens[1], "dns")) {
325 properties->dns_servers.push_back(tokens[2]);
326 }
327}
328
Darin Petkov60596742012-03-05 12:17:17 +0100329// static
330IPConfig::Route *OpenVPNDriver::GetRouteOptionEntry(
331 const string &prefix, const string &key, RouteOptions *routes) {
332 int order = 0;
333 if (!StartsWithASCII(key, prefix, false) ||
334 !base::StringToInt(key.substr(prefix.size()), &order)) {
335 return NULL;
336 }
337 return &(*routes)[order];
338}
339
340// static
341void OpenVPNDriver::ParseRouteOption(
342 const string &key, const string &value, RouteOptions *routes) {
343 IPConfig::Route *route = GetRouteOptionEntry("network_", key, routes);
344 if (route) {
345 route->host = value;
346 return;
347 }
348 route = GetRouteOptionEntry("netmask_", key, routes);
349 if (route) {
350 route->netmask = value;
351 return;
352 }
353 route = GetRouteOptionEntry("gateway_", key, routes);
354 if (route) {
355 route->gateway = value;
356 return;
357 }
358 LOG(WARNING) << "Unknown route option ignored: " << key;
359}
360
361// static
362void OpenVPNDriver::SetRoutes(const RouteOptions &routes,
363 IPConfig::Properties *properties) {
364 for (RouteOptions::const_iterator it = routes.begin();
365 it != routes.end(); ++it) {
366 const IPConfig::Route &route = it->second;
367 if (route.host.empty() || route.netmask.empty() || route.gateway.empty()) {
368 LOG(WARNING) << "Ignoring incomplete route: " << it->first;
369 continue;
370 }
371 properties->routes.push_back(route);
372 }
373}
374
Darin Petkov79d74c92012-03-07 17:20:32 +0100375void OpenVPNDriver::Connect(const VPNServiceRefPtr &service,
376 Error *error) {
377 service_ = service;
378 service_->SetState(Service::kStateConfiguring);
Darin Petkovf20994f2012-03-05 16:12:19 +0100379 if (!device_info_->CreateTunnelInterface(&tunnel_interface_)) {
380 Error::PopulateAndLog(
381 error, Error::kInternalError, "Could not create tunnel interface.");
Darin Petkov3f9131c2012-03-20 11:37:32 +0100382 Cleanup(Service::kStateFailure);
Darin Petkovf20994f2012-03-05 16:12:19 +0100383 }
384 // Wait for the ClaimInterface callback to continue the connection process.
Darin Petkov33af05c2012-02-28 10:10:30 +0100385}
386
Darin Petkovfe6a9372012-02-28 16:25:06 +0100387void OpenVPNDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkov7f060332012-03-14 11:46:47 +0100388 string vpnhost = args_.LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100389 if (vpnhost.empty()) {
390 Error::PopulateAndLog(
391 error, Error::kInvalidArguments, "VPN host not specified.");
392 return;
393 }
394 options->push_back("--client");
395 options->push_back("--tls-client");
396 options->push_back("--remote");
397 options->push_back(vpnhost);
398 options->push_back("--nobind");
399 options->push_back("--persist-key");
400 options->push_back("--persist-tun");
401
Darin Petkovf20994f2012-03-05 16:12:19 +0100402 CHECK(!tunnel_interface_.empty());
Paul Stewartca6abd42012-03-01 15:45:29 -0800403 options->push_back("--dev");
404 options->push_back(tunnel_interface_);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100405 options->push_back("--dev-type");
406 options->push_back("tun");
407 options->push_back("--syslog");
408
409 // TODO(petkov): Enable verbosity based on shill logging options too.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100410 AppendValueOption(kOpenVPNVerbProperty, "--verb", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100411
Darin Petkovf3c71d72012-03-21 12:32:15 +0100412 AppendValueOption(kVPNMTUProperty, "--mtu", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100413 AppendValueOption(flimflam::kOpenVPNProtoProperty, "--proto", options);
414 AppendValueOption(flimflam::kOpenVPNPortProperty, "--port", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100415 AppendValueOption(kOpenVPNTLSAuthProperty, "--tls-auth", options);
Darin Petkov1fa81942012-04-02 11:38:08 +0200416 {
417 string contents =
418 args_.LookupString(flimflam::kOpenVPNTLSAuthContentsProperty, "");
419 if (!contents.empty()) {
420 if (!file_util::CreateTemporaryFile(&tls_auth_file_) ||
421 file_util::WriteFile(
422 tls_auth_file_, contents.data(), contents.size()) !=
423 static_cast<int>(contents.size())) {
424 Error::PopulateAndLog(
425 error, Error::kInternalError, "Unable to setup tls-auth file.");
426 return;
427 }
428 options->push_back("--tls-auth");
429 options->push_back(tls_auth_file_.value());
430 }
431 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100432 AppendValueOption(
433 flimflam::kOpenVPNTLSRemoteProperty, "--tls-remote", options);
434 AppendValueOption(flimflam::kOpenVPNCipherProperty, "--cipher", options);
435 AppendValueOption(flimflam::kOpenVPNAuthProperty, "--auth", options);
436 AppendFlag(flimflam::kOpenVPNAuthNoCacheProperty, "--auth-nocache", options);
437 AppendValueOption(
438 flimflam::kOpenVPNAuthRetryProperty, "--auth-retry", options);
439 AppendFlag(flimflam::kOpenVPNCompLZOProperty, "--comp-lzo", options);
440 AppendFlag(flimflam::kOpenVPNCompNoAdaptProperty, "--comp-noadapt", options);
441 AppendFlag(
442 flimflam::kOpenVPNPushPeerInfoProperty, "--push-peer-info", options);
443 AppendValueOption(flimflam::kOpenVPNRenegSecProperty, "--reneg-sec", options);
444 AppendValueOption(flimflam::kOpenVPNShaperProperty, "--shaper", options);
445 AppendValueOption(flimflam::kOpenVPNServerPollTimeoutProperty,
446 "--server-poll-timeout", options);
447
Darin Petkove0d5dd12012-04-04 16:10:48 +0200448 if (!InitNSSOptions(options, error)) {
449 return;
Darin Petkov3c5e4dc2012-04-02 14:44:27 +0200450 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100451
452 // Client-side ping support.
Darin Petkovf3c71d72012-03-21 12:32:15 +0100453 AppendValueOption(kOpenVPNPingProperty, "--ping", options);
454 AppendValueOption(kOpenVPNPingExitProperty, "--ping-exit", options);
455 AppendValueOption(kOpenVPNPingRestartProperty, "--ping-restart", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100456
457 AppendValueOption(flimflam::kOpenVPNCaCertProperty, "--ca", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100458 AppendValueOption(kOpenVPNCertProperty, "--cert", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100459 AppendValueOption(
460 flimflam::kOpenVPNNsCertTypeProperty, "--ns-cert-type", options);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100461 AppendValueOption(kOpenVPNKeyProperty, "--key", options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100462
Darin Petkove0d5dd12012-04-04 16:10:48 +0200463 InitPKCS11Options(options);
Darin Petkovfe6a9372012-02-28 16:25:06 +0100464
465 // TLS suport.
Darin Petkov7f060332012-03-14 11:46:47 +0100466 string remote_cert_tls =
467 args_.LookupString(flimflam::kOpenVPNRemoteCertTLSProperty, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100468 if (remote_cert_tls.empty()) {
469 remote_cert_tls = "server";
470 }
471 if (remote_cert_tls != "none") {
472 options->push_back("--remote-cert-tls");
473 options->push_back(remote_cert_tls);
474 }
475
476 // This is an undocumented command line argument that works like a .cfg file
477 // entry. TODO(sleffler): Maybe roll this into --tls-auth?
478 AppendValueOption(
479 flimflam::kOpenVPNKeyDirectionProperty, "--key-direction", options);
480 // TODO(sleffler): Support more than one eku parameter.
481 AppendValueOption(
482 flimflam::kOpenVPNRemoteCertEKUProperty, "--remote-cert-eku", options);
483 AppendValueOption(
484 flimflam::kOpenVPNRemoteCertKUProperty, "--remote-cert-ku", options);
485
Darin Petkove0d5dd12012-04-04 16:10:48 +0200486 if (!InitManagementChannelOptions(options, error)) {
Darin Petkov46463022012-03-29 14:57:32 +0200487 return;
488 }
Darin Petkovfe6a9372012-02-28 16:25:06 +0100489
Darin Petkova9b1fed2012-02-29 11:49:05 +0100490 // Setup openvpn-script options and RPC information required to send back
491 // Layer 3 configuration.
492 options->push_back("--setenv");
493 options->push_back("CONNMAN_BUSNAME");
494 options->push_back(rpc_task_->GetRpcConnectionIdentifier());
495 options->push_back("--setenv");
496 options->push_back("CONNMAN_INTERFACE");
497 options->push_back(rpc_task_->GetRpcInterfaceIdentifier());
498 options->push_back("--setenv");
499 options->push_back("CONNMAN_PATH");
500 options->push_back(rpc_task_->GetRpcIdentifier());
501 options->push_back("--script-security");
502 options->push_back("2");
503 options->push_back("--up");
504 options->push_back(kOpenVPNScript);
505 options->push_back("--up-restart");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100506
507 // Disable openvpn handling since we do route+ifconfig work.
508 options->push_back("--route-noexec");
509 options->push_back("--ifconfig-noexec");
510
511 // Drop root privileges on connection and enable callback scripts to send
512 // notify messages.
513 options->push_back("--user");
514 options->push_back("openvpn");
515 options->push_back("--group");
516 options->push_back("openvpn");
517}
518
Darin Petkove0d5dd12012-04-04 16:10:48 +0200519bool OpenVPNDriver::InitNSSOptions(vector<string> *options, Error *error) {
520 string ca_cert = args_.LookupString(flimflam::kOpenVPNCaCertNSSProperty, "");
521 if (!ca_cert.empty()) {
522 if (!args_.LookupString(flimflam::kOpenVPNCaCertProperty, "").empty()) {
523 Error::PopulateAndLog(error,
524 Error::kInvalidArguments,
525 "Can't specify both CACert and CACertNSS.");
526 return false;
527 }
528 const string &vpnhost = args_.GetString(flimflam::kProviderHostProperty);
529 vector<char> id(vpnhost.begin(), vpnhost.end());
530 FilePath certfile = nss_->GetPEMCertfile(ca_cert, id);
531 if (certfile.empty()) {
532 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
533 } else {
534 options->push_back("--ca");
535 options->push_back(certfile.value());
536 }
537 }
538 return true;
539}
540
541void OpenVPNDriver::InitPKCS11Options(vector<string> *options) {
542 string id = args_.LookupString(flimflam::kOpenVPNClientCertIdProperty, "");
543 if (!id.empty()) {
544 string provider =
545 args_.LookupString(flimflam::kOpenVPNProviderProperty, "");
546 if (provider.empty()) {
547 provider = kDefaultPKCS11Provider;
548 }
549 options->push_back("--pkcs11-providers");
550 options->push_back(provider);
551 options->push_back("--pkcs11-id");
552 options->push_back(id);
553 }
554}
555
556bool OpenVPNDriver::InitManagementChannelOptions(
557 vector<string> *options, Error *error) {
558 if (!management_server_->Start(dispatcher_, &sockets_, options)) {
559 Error::PopulateAndLog(
560 error, Error::kInternalError, "Unable to setup management channel.");
561 return false;
562 }
563 return true;
564}
565
Darin Petkov46463022012-03-29 14:57:32 +0200566bool OpenVPNDriver::AppendValueOption(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100567 const string &property, const string &option, vector<string> *options) {
Darin Petkov7f060332012-03-14 11:46:47 +0100568 string value = args_.LookupString(property, "");
Darin Petkovfe6a9372012-02-28 16:25:06 +0100569 if (!value.empty()) {
570 options->push_back(option);
571 options->push_back(value);
Darin Petkov46463022012-03-29 14:57:32 +0200572 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100573 }
Darin Petkov46463022012-03-29 14:57:32 +0200574 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100575}
576
Darin Petkov46463022012-03-29 14:57:32 +0200577bool OpenVPNDriver::AppendFlag(
Darin Petkovfe6a9372012-02-28 16:25:06 +0100578 const string &property, const string &option, vector<string> *options) {
579 if (args_.ContainsString(property)) {
580 options->push_back(option);
Darin Petkov46463022012-03-29 14:57:32 +0200581 return true;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100582 }
Darin Petkov46463022012-03-29 14:57:32 +0200583 return false;
Darin Petkovfe6a9372012-02-28 16:25:06 +0100584}
585
Paul Stewartce4ec192012-03-14 12:53:46 -0700586bool OpenVPNDriver::PinHostRoute(const IPConfig::Properties &properties) {
587 if (properties.gateway.empty() || properties.trusted_ip.empty()) {
588 return false;
589 }
590
591 IPAddress trusted_ip(properties.address_family);
592 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
593 LOG(ERROR) << "Failed to parse trusted_ip "
594 << properties.trusted_ip << "; ignored.";
595 return false;
596 }
597
598 ServiceRefPtr default_service = manager_->GetDefaultService();
599 if (!default_service) {
600 LOG(ERROR) << "No default service exists.";
601 return false;
602 }
603
604 CHECK(default_service->connection());
605 return default_service->connection()->RequestHostRoute(trusted_ip);
606}
607
Darin Petkov6aa21872012-03-09 16:10:19 +0100608void OpenVPNDriver::Disconnect() {
Darin Petkov271fe522012-03-27 13:47:29 +0200609 VLOG(2) << __func__;
Darin Petkov3f9131c2012-03-20 11:37:32 +0100610 Cleanup(Service::kStateIdle);
Darin Petkov6aa21872012-03-09 16:10:19 +0100611}
612
Darin Petkov271fe522012-03-27 13:47:29 +0200613void OpenVPNDriver::OnReconnecting() {
614 VLOG(2) << __func__;
615 if (device_) {
616 device_->OnDisconnected();
617 }
618 if (service_) {
619 service_->SetState(Service::kStateAssociating);
620 }
621}
622
Darin Petkovf3c71d72012-03-21 12:32:15 +0100623bool OpenVPNDriver::Load(StoreInterface *storage, const string &storage_id) {
Paul Stewartebd38562012-03-23 13:06:40 -0700624 for (size_t i = 0; i < arraysize(kProperties); i++) {
Darin Petkovf3c71d72012-03-21 12:32:15 +0100625 const string property = kProperties[i].property;
626 string value;
627 bool loaded = kProperties[i].crypted ?
628 storage->GetCryptedString(storage_id, property, &value) :
629 storage->GetString(storage_id, property, &value);
630 if (loaded) {
631 args_.SetString(property, value);
Paul Stewart141983b2012-03-23 07:58:32 -0700632 } else {
633 args_.RemoveString(property);
Darin Petkovf3c71d72012-03-21 12:32:15 +0100634 }
635 }
636 return true;
637}
638
639bool OpenVPNDriver::Save(StoreInterface *storage, const string &storage_id) {
Paul Stewartebd38562012-03-23 13:06:40 -0700640 for (size_t i = 0; i < arraysize(kProperties); i++) {
Darin Petkovf3c71d72012-03-21 12:32:15 +0100641 const string property = kProperties[i].property;
642 const string value = args_.LookupString(property, "");
643 if (value.empty()) {
644 storage->DeleteKey(storage_id, property);
645 } else if (kProperties[i].crypted) {
646 storage->SetCryptedString(storage_id, property, value);
647 } else {
648 storage->SetString(storage_id, property, value);
649 }
650 }
651 return true;
652}
653
Paul Stewartebd38562012-03-23 13:06:40 -0700654void OpenVPNDriver::InitPropertyStore(PropertyStore *store) {
655 for (size_t i = 0; i < arraysize(kProperties); i++) {
656 store->RegisterDerivedString(
657 kProperties[i].property,
658 StringAccessor(
659 new CustomMappedAccessor<OpenVPNDriver, string, size_t>(
660 this,
661 &OpenVPNDriver::ClearMappedProperty,
662 &OpenVPNDriver::GetMappedProperty,
663 &OpenVPNDriver::SetMappedProperty,
664 i)));
665 }
666
667 // TODO(pstew): Add the PassphraseRequired and PSKRequired properties.
668 // crosbug.com/27323
669}
670
671void OpenVPNDriver::ClearMappedProperty(const size_t &index,
672 Error *error) {
673 CHECK(index < arraysize(kProperties));
674 if (args_.ContainsString(kProperties[index].property)) {
675 args_.RemoveString(kProperties[index].property);
676 } else {
677 error->Populate(Error::kNotFound, "Property is not set");
678 }
679}
680
681string OpenVPNDriver::GetMappedProperty(const size_t &index,
682 Error *error) {
683 CHECK(index < arraysize(kProperties));
684 if (kProperties[index].crypted) {
685 error->Populate(Error::kPermissionDenied, "Property is write-only");
686 return string();
687 }
688
689 if (!args_.ContainsString(kProperties[index].property)) {
690 error->Populate(Error::kNotFound, "Property is not set");
691 return string();
692 }
693
694 return args_.LookupString(kProperties[index].property, "");
695}
696
697void OpenVPNDriver::SetMappedProperty(const size_t &index,
698 const string &value,
699 Error *error) {
700 CHECK(index < arraysize(kProperties));
701 args_.SetString(kProperties[index].property, value);
702}
703
704
Darin Petkov33af05c2012-02-28 10:10:30 +0100705} // namespace shill