blob: 8a0fdcb1a15e61f11f974a3cd19792f4fbfaefc8 [file] [log] [blame]
Darin Petkov7476a262012-04-12 16:30:46 +02001// 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/l2tp_ipsec_driver.h"
6
Darin Petkovf7ef50a2012-04-16 20:54:31 +02007#include <base/file_util.h>
Darin Petkov7476a262012-04-12 16:30:46 +02008#include <base/logging.h>
Darin Petkov209e6292012-04-20 11:33:32 +02009#include <base/string_util.h>
Darin Petkov7476a262012-04-12 16:30:46 +020010#include <chromeos/dbus/service_constants.h>
11
Darin Petkovf8046b82012-04-24 16:29:23 +020012#include "shill/device_info.h"
Darin Petkovf7ef50a2012-04-16 20:54:31 +020013#include "shill/error.h"
14#include "shill/manager.h"
15#include "shill/nss.h"
Darin Petkov5a850472012-06-06 15:44:24 +020016#include "shill/process_killer.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070017#include "shill/scope_logger.h"
Darin Petkovf8046b82012-04-24 16:29:23 +020018#include "shill/vpn.h"
Darin Petkov209e6292012-04-20 11:33:32 +020019#include "shill/vpn_service.h"
Darin Petkovf7ef50a2012-04-16 20:54:31 +020020
Darin Petkov5a850472012-06-06 15:44:24 +020021using base::Closure;
Darin Petkov209e6292012-04-20 11:33:32 +020022using std::map;
Darin Petkov7476a262012-04-12 16:30:46 +020023using std::string;
Darin Petkovf7ef50a2012-04-16 20:54:31 +020024using std::vector;
Darin Petkov7476a262012-04-12 16:30:46 +020025
26namespace shill {
27
Darin Petkovf7ef50a2012-04-16 20:54:31 +020028namespace {
29const char kL2TPIPSecIPSecTimeoutProperty[] = "L2TPIPsec.IPsecTimeout";
30const char kL2TPIPSecLeftProtoPortProperty[] = "L2TPIPsec.LeftProtoPort";
31const char kL2TPIPSecLengthBitProperty[] = "L2TPIPsec.LengthBit";
32const char kL2TPIPSecPFSProperty[] = "L2TPIPsec.PFS";
33const char kL2TPIPSecRefusePapProperty[] = "L2TPIPsec.RefusePap";
34const char kL2TPIPSecRekeyProperty[] = "L2TPIPsec.Rekey";
35const char kL2TPIPSecRequireAuthProperty[] = "L2TPIPsec.RequireAuth";
36const char kL2TPIPSecRequireChapProperty[] = "L2TPIPsec.RequireChap";
37const char kL2TPIPSecRightProtoPortProperty[] = "L2TPIPsec.RightProtoPort";
38} // namespace
39
40// static
41const char L2TPIPSecDriver::kPPPDPlugin[] = SCRIPTDIR "/libppp-plugin.so";
Darin Petkovd4325392012-04-23 15:48:22 +020042// static
Darin Petkov209e6292012-04-20 11:33:32 +020043const char L2TPIPSecDriver::kL2TPIPSecVPNPath[] = "/usr/sbin/l2tpipsec_vpn";
Darin Petkovd4325392012-04-23 15:48:22 +020044// static
45const VPNDriver::Property L2TPIPSecDriver::kProperties[] = {
46 { flimflam::kL2tpIpsecCaCertNssProperty, 0 },
47 { flimflam::kL2tpIpsecClientCertIdProperty, 0 },
48 { flimflam::kL2tpIpsecClientCertSlotProperty, 0 },
Darin Petkov02236552012-06-11 13:15:19 +020049 { flimflam::kL2tpIpsecPasswordProperty,
50 Property::kCredential | Property::kWriteOnly },
Darin Petkovcb715292012-04-25 13:04:37 +020051 { flimflam::kL2tpIpsecPinProperty, Property::kCredential },
52 { flimflam::kL2tpIpsecPskProperty, Property::kCredential },
Darin Petkovd4325392012-04-23 15:48:22 +020053 { flimflam::kL2tpIpsecUserProperty, 0 },
Darin Petkov2c773c22012-04-26 12:54:11 +020054 { flimflam::kProviderHostProperty, 0 },
55 { flimflam::kProviderNameProperty, 0 },
56 { flimflam::kProviderTypeProperty, 0 },
Darin Petkovd4325392012-04-23 15:48:22 +020057 { kL2TPIPSecIPSecTimeoutProperty, 0 },
58 { kL2TPIPSecLeftProtoPortProperty, 0 },
59 { kL2TPIPSecLengthBitProperty, 0 },
60 { kL2TPIPSecPFSProperty, 0 },
61 { kL2TPIPSecRefusePapProperty, 0 },
62 { kL2TPIPSecRekeyProperty, 0 },
63 { kL2TPIPSecRequireAuthProperty, 0 },
64 { kL2TPIPSecRequireChapProperty, 0 },
65 { kL2TPIPSecRightProtoPortProperty, 0 },
66};
Darin Petkovf7ef50a2012-04-16 20:54:31 +020067
Darin Petkov209e6292012-04-20 11:33:32 +020068L2TPIPSecDriver::L2TPIPSecDriver(ControlInterface *control,
Darin Petkovf8046b82012-04-24 16:29:23 +020069 EventDispatcher *dispatcher,
70 Metrics *metrics,
Darin Petkov209e6292012-04-20 11:33:32 +020071 Manager *manager,
Darin Petkovf8046b82012-04-24 16:29:23 +020072 DeviceInfo *device_info,
Darin Petkov209e6292012-04-20 11:33:32 +020073 GLib *glib)
Darin Petkov602303f2012-06-06 12:15:59 +020074 : VPNDriver(dispatcher, manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +020075 control_(control),
Darin Petkovf8046b82012-04-24 16:29:23 +020076 metrics_(metrics),
77 device_info_(device_info),
Darin Petkov209e6292012-04-20 11:33:32 +020078 glib_(glib),
79 nss_(NSS::GetInstance()),
Darin Petkov5a850472012-06-06 15:44:24 +020080 process_killer_(ProcessKiller::GetInstance()),
Darin Petkov209e6292012-04-20 11:33:32 +020081 pid_(0),
82 child_watch_tag_(0) {}
Darin Petkov7476a262012-04-12 16:30:46 +020083
Darin Petkov209e6292012-04-20 11:33:32 +020084L2TPIPSecDriver::~L2TPIPSecDriver() {
85 Cleanup(Service::kStateIdle);
86}
Darin Petkov7476a262012-04-12 16:30:46 +020087
88bool L2TPIPSecDriver::ClaimInterface(const string &link_name,
89 int interface_index) {
90 // TODO(petkov): crosbug.com/26843.
91 NOTIMPLEMENTED();
92 return false;
93}
94
95void L2TPIPSecDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov602303f2012-06-06 12:15:59 +020096 StartConnectTimeout();
Darin Petkov209e6292012-04-20 11:33:32 +020097 service_ = service;
98 service_->SetState(Service::kStateConfiguring);
99 rpc_task_.reset(new RPCTask(control_, this));
100 if (!SpawnL2TPIPSecVPN(error)) {
101 Cleanup(Service::kStateFailure);
102 }
Darin Petkov7476a262012-04-12 16:30:46 +0200103}
104
105void L2TPIPSecDriver::Disconnect() {
Darin Petkova0e645e2012-04-25 11:38:59 +0200106 SLOG(VPN, 2) << __func__;
107 Cleanup(Service::kStateIdle);
Darin Petkov7476a262012-04-12 16:30:46 +0200108}
109
Darin Petkov5eb05422012-05-11 15:45:25 +0200110void L2TPIPSecDriver::OnConnectionDisconnected() {
Darin Petkov602303f2012-06-06 12:15:59 +0200111 LOG(ERROR) << "VPN connection disconnected.";
Darin Petkov5eb05422012-05-11 15:45:25 +0200112 Cleanup(Service::kStateFailure);
113}
114
Darin Petkov7476a262012-04-12 16:30:46 +0200115string L2TPIPSecDriver::GetProviderType() const {
116 return flimflam::kProviderL2tpIpsec;
117}
118
Darin Petkov209e6292012-04-20 11:33:32 +0200119void L2TPIPSecDriver::Cleanup(Service::ConnectState state) {
120 SLOG(VPN, 2) << __func__
121 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov602303f2012-06-06 12:15:59 +0200122 StopConnectTimeout();
Darin Petkov0e9735d2012-04-24 12:33:45 +0200123 DeletePSKFile();
Darin Petkov209e6292012-04-20 11:33:32 +0200124 if (child_watch_tag_) {
125 glib_->SourceRemove(child_watch_tag_);
126 child_watch_tag_ = 0;
Darin Petkov209e6292012-04-20 11:33:32 +0200127 }
128 if (pid_) {
Darin Petkov5a850472012-06-06 15:44:24 +0200129 process_killer_->Kill(pid_, Closure());
Darin Petkov209e6292012-04-20 11:33:32 +0200130 pid_ = 0;
131 }
Darin Petkovf8046b82012-04-24 16:29:23 +0200132 if (device_) {
133 device_->OnDisconnected();
134 device_->SetEnabled(false);
135 device_ = NULL;
136 }
Darin Petkov209e6292012-04-20 11:33:32 +0200137 rpc_task_.reset();
138 if (service_) {
139 service_->SetState(state);
140 service_ = NULL;
141 }
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200142}
143
Darin Petkov0e9735d2012-04-24 12:33:45 +0200144void L2TPIPSecDriver::DeletePSKFile() {
145 if (!psk_file_.empty()) {
146 file_util::Delete(psk_file_, false);
147 psk_file_.clear();
148 }
149}
150
Darin Petkov209e6292012-04-20 11:33:32 +0200151bool L2TPIPSecDriver::SpawnL2TPIPSecVPN(Error *error) {
152 SLOG(VPN, 2) << __func__;
153
154 vector<string> options;
155 if (!InitOptions(&options, error)) {
156 return false;
157 }
158 SLOG(VPN, 2) << "L2TP/IPSec VPN process options: "
159 << JoinString(options, ' ');
160
161 // TODO(petkov): This code needs to be abstracted away in a separate external
162 // process module (crosbug.com/27131).
163 vector<char *> process_args;
164 process_args.push_back(const_cast<char *>(kL2TPIPSecVPNPath));
165 for (vector<string>::const_iterator it = options.begin();
166 it != options.end(); ++it) {
167 process_args.push_back(const_cast<char *>(it->c_str()));
168 }
169 process_args.push_back(NULL);
170
171 vector<string> environment;
172 InitEnvironment(&environment);
173
174 vector<char *> process_env;
175 for (vector<string>::const_iterator it = environment.begin();
176 it != environment.end(); ++it) {
177 process_env.push_back(const_cast<char *>(it->c_str()));
178 }
179 process_env.push_back(NULL);
180
181 CHECK(!pid_);
182 // Redirect all l2tp/ipsec output to stderr.
183 int stderr_fd = fileno(stderr);
184 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
185 process_env.data(),
186 G_SPAWN_DO_NOT_REAP_CHILD,
187 NULL,
188 NULL,
189 &pid_,
190 NULL,
191 &stderr_fd,
192 &stderr_fd,
193 NULL)) {
194 Error::PopulateAndLog(error, Error::kInternalError,
195 string("Unable to spawn: ") + process_args[0]);
196 return false;
197 }
198 CHECK(!child_watch_tag_);
199 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnL2TPIPSecVPNDied, this);
200 return true;
201}
202
203void L2TPIPSecDriver::InitEnvironment(vector<string> *environment) {
204 environment->push_back(
205 "CONNMAN_BUSNAME=" + rpc_task_->GetRpcConnectionIdentifier());
206 environment->push_back(
207 "CONNMAN_INTERFACE=" + rpc_task_->GetRpcInterfaceIdentifier());
208 environment->push_back(
209 "CONNMAN_PATH=" + rpc_task_->GetRpcIdentifier());
210}
211
212bool L2TPIPSecDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkov01c66042012-04-26 11:10:45 +0200213 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200214 if (vpnhost.empty()) {
215 Error::PopulateAndLog(
216 error, Error::kInvalidArguments, "VPN host not specified.");
Darin Petkov209e6292012-04-20 11:33:32 +0200217 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200218 }
219
220 if (!InitPSKOptions(options, error)) {
Darin Petkov209e6292012-04-20 11:33:32 +0200221 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200222 }
223
224 options->push_back("--remote_host");
225 options->push_back(vpnhost);
226 options->push_back("--pppd_plugin");
227 options->push_back(kPPPDPlugin);
228 // Disable pppd from configuring IP addresses, routes, DNS.
229 options->push_back("--nosystemconfig");
230
231 InitNSSOptions(options);
232
233 AppendValueOption(flimflam::kL2tpIpsecClientCertIdProperty,
234 "--client_cert_id", options);
235 AppendValueOption(flimflam::kL2tpIpsecClientCertSlotProperty,
236 "--client_cert_slot", options);
237 AppendValueOption(flimflam::kL2tpIpsecPinProperty, "--user_pin", options);
238 AppendValueOption(flimflam::kL2tpIpsecUserProperty, "--user", options);
239 AppendValueOption(kL2TPIPSecIPSecTimeoutProperty, "--ipsec_timeout", options);
240 AppendValueOption(kL2TPIPSecLeftProtoPortProperty,
241 "--leftprotoport", options);
242 AppendFlag(kL2TPIPSecPFSProperty, "--pfs", "--nopfs", options);
243 AppendFlag(kL2TPIPSecRekeyProperty, "--rekey", "--norekey", options);
244 AppendValueOption(kL2TPIPSecRightProtoPortProperty,
245 "--rightprotoport", options);
246 AppendFlag(kL2TPIPSecRequireChapProperty,
247 "--require_chap", "--norequire_chap", options);
248 AppendFlag(kL2TPIPSecRefusePapProperty,
249 "--refuse_pap", "--norefuse_pap", options);
250 AppendFlag(kL2TPIPSecRequireAuthProperty,
251 "--require_authentication", "--norequire_authentication", options);
252 AppendFlag(kL2TPIPSecLengthBitProperty,
253 "--length_bit", "--nolength_bit", options);
Darin Petkov209e6292012-04-20 11:33:32 +0200254 if (SLOG_IS_ON(VPN, 0)) {
255 options->push_back("--debug");
256 }
257 return true;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200258}
259
260bool L2TPIPSecDriver::InitPSKOptions(vector<string> *options, Error *error) {
Darin Petkov01c66042012-04-26 11:10:45 +0200261 string psk = args()->LookupString(flimflam::kL2tpIpsecPskProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200262 if (!psk.empty()) {
263 if (!file_util::CreateTemporaryFileInDir(
Darin Petkov0e9735d2012-04-24 12:33:45 +0200264 manager()->run_path(), &psk_file_) ||
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200265 chmod(psk_file_.value().c_str(), S_IRUSR | S_IWUSR) ||
266 file_util::WriteFile(psk_file_, psk.data(), psk.size()) !=
267 static_cast<int>(psk.size())) {
268 Error::PopulateAndLog(
269 error, Error::kInternalError, "Unable to setup psk file.");
270 return false;
271 }
272 options->push_back("--psk_file");
273 options->push_back(psk_file_.value());
274 }
275 return true;
276}
277
278void L2TPIPSecDriver::InitNSSOptions(vector<string> *options) {
279 string ca_cert =
Darin Petkov01c66042012-04-26 11:10:45 +0200280 args()->LookupString(flimflam::kL2tpIpsecCaCertNssProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200281 if (!ca_cert.empty()) {
Darin Petkov01c66042012-04-26 11:10:45 +0200282 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200283 vector<char> id(vpnhost.begin(), vpnhost.end());
284 FilePath certfile = nss_->GetDERCertfile(ca_cert, id);
285 if (certfile.empty()) {
286 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
287 } else {
288 options->push_back("--server_ca_file");
289 options->push_back(certfile.value());
290 }
291 }
292}
293
294bool L2TPIPSecDriver::AppendValueOption(
295 const string &property, const string &option, vector<string> *options) {
Darin Petkov01c66042012-04-26 11:10:45 +0200296 string value = args()->LookupString(property, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200297 if (!value.empty()) {
298 options->push_back(option);
299 options->push_back(value);
300 return true;
301 }
302 return false;
303}
304
305bool L2TPIPSecDriver::AppendFlag(const string &property,
306 const string &true_option,
307 const string &false_option,
308 vector<string> *options) {
Darin Petkov01c66042012-04-26 11:10:45 +0200309 string value = args()->LookupString(property, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200310 if (!value.empty()) {
311 options->push_back(value == "true" ? true_option : false_option);
312 return true;
313 }
314 return false;
315}
316
Darin Petkov209e6292012-04-20 11:33:32 +0200317// static
318void L2TPIPSecDriver::OnL2TPIPSecVPNDied(GPid pid, gint status, gpointer data) {
319 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
320 L2TPIPSecDriver *me = reinterpret_cast<L2TPIPSecDriver *>(data);
321 me->child_watch_tag_ = 0;
322 CHECK_EQ(pid, me->pid_);
Darin Petkov5a850472012-06-06 15:44:24 +0200323 me->pid_ = 0;
Darin Petkov209e6292012-04-20 11:33:32 +0200324 me->Cleanup(Service::kStateFailure);
325 // TODO(petkov): Figure if we need to restart the connection.
326}
327
328void L2TPIPSecDriver::GetLogin(string *user, string *password) {
Darin Petkov602303f2012-06-06 12:15:59 +0200329 LOG(INFO) << "Login requested.";
Darin Petkov209e6292012-04-20 11:33:32 +0200330 string user_property =
Darin Petkov01c66042012-04-26 11:10:45 +0200331 args()->LookupString(flimflam::kL2tpIpsecUserProperty, "");
Darin Petkov209e6292012-04-20 11:33:32 +0200332 if (user_property.empty()) {
333 LOG(ERROR) << "User not set.";
334 return;
335 }
336 string password_property =
Darin Petkov01c66042012-04-26 11:10:45 +0200337 args()->LookupString(flimflam::kL2tpIpsecPasswordProperty, "");
Darin Petkov209e6292012-04-20 11:33:32 +0200338 if (password_property.empty()) {
339 LOG(ERROR) << "Password not set.";
340 return;
341 }
342 *user = user_property;
343 *password = password_property;
344}
345
Darin Petkov0e9735d2012-04-24 12:33:45 +0200346void L2TPIPSecDriver::ParseIPConfiguration(
347 const map<string, string> &configuration,
348 IPConfig::Properties *properties,
349 string *interface_name) {
350 properties->address_family = IPAddress::kFamilyIPv4;
351 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
352 properties->address_family);
353 for (map<string, string>::const_iterator it = configuration.begin();
354 it != configuration.end(); ++it) {
355 const string &key = it->first;
356 const string &value = it->second;
357 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
358 if (key == "INTERNAL_IP4_ADDRESS") {
359 properties->address = value;
360 } else if (key == "EXTERNAL_IP4_ADDRESS") {
361 properties->peer_address = value;
362 } else if (key == "GATEWAY_ADDRESS") {
363 properties->gateway = value;
364 } else if (key == "DNS1") {
365 properties->dns_servers.insert(properties->dns_servers.begin(), value);
366 } else if (key == "DNS2") {
367 properties->dns_servers.push_back(value);
368 } else if (key == "INTERNAL_IFNAME") {
369 *interface_name = value;
370 } else if (key == "LNS_ADDRESS") {
371 properties->trusted_ip = value;
372 } else {
373 SLOG(VPN, 2) << "Key ignored.";
374 }
375 }
376}
377
Darin Petkov209e6292012-04-20 11:33:32 +0200378void L2TPIPSecDriver::Notify(
379 const string &reason, const map<string, string> &dict) {
Darin Petkov602303f2012-06-06 12:15:59 +0200380 LOG(INFO) << "IP configuration received: " << reason;
Darin Petkov0e9735d2012-04-24 12:33:45 +0200381
382 if (reason != "connect") {
Darin Petkova0e645e2012-04-25 11:38:59 +0200383 device_->OnDisconnected();
Darin Petkov0e9735d2012-04-24 12:33:45 +0200384 return;
385 }
386
Darin Petkovf8046b82012-04-24 16:29:23 +0200387 DeletePSKFile();
388
Darin Petkov0e9735d2012-04-24 12:33:45 +0200389 IPConfig::Properties properties;
390 string interface_name;
391 ParseIPConfiguration(dict, &properties, &interface_name);
Darin Petkovf8046b82012-04-24 16:29:23 +0200392
393 int interface_index = device_info_->GetIndex(interface_name);
394 if (interface_index < 0) {
395 // TODO(petkov): Consider handling the race when the RTNL notification about
396 // the new PPP device has not been received yet. We can keep the IP
397 // configuration and apply it when ClaimInterface is invoked.
398 NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
399 return;
400 }
401
402 if (!device_) {
Darin Petkov602303f2012-06-06 12:15:59 +0200403 device_ = new VPN(control_, dispatcher(), metrics_, manager(),
Darin Petkovf8046b82012-04-24 16:29:23 +0200404 interface_name, interface_index);
405 }
406 device_->SetEnabled(true);
407 device_->SelectService(service_);
Darin Petkovf8046b82012-04-24 16:29:23 +0200408 device_->UpdateIPConfig(properties);
Darin Petkov602303f2012-06-06 12:15:59 +0200409 StopConnectTimeout();
Darin Petkov209e6292012-04-20 11:33:32 +0200410}
411
Darin Petkovb536a742012-04-26 11:31:28 +0200412KeyValueStore L2TPIPSecDriver::GetProvider(Error *error) {
413 SLOG(VPN, 2) << __func__;
414 KeyValueStore props = VPNDriver::GetProvider(error);
415 props.SetBool(flimflam::kPassphraseRequiredProperty,
416 args()->LookupString(
417 flimflam::kL2tpIpsecPasswordProperty, "").empty());
418 props.SetBool(flimflam::kL2tpIpsecPskRequiredProperty,
419 args()->LookupString(
420 flimflam::kL2tpIpsecPskProperty, "").empty());
421 return props;
422}
423
Darin Petkov7476a262012-04-12 16:30:46 +0200424} // namespace shill