blob: 1a001cb417a7704f6512153c589240660175dfe1 [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"
Ben Chanfad4a0b2012-04-18 15:49:59 -070016#include "shill/scope_logger.h"
Darin Petkovf8046b82012-04-24 16:29:23 +020017#include "shill/vpn.h"
Darin Petkov209e6292012-04-20 11:33:32 +020018#include "shill/vpn_service.h"
Darin Petkovf7ef50a2012-04-16 20:54:31 +020019
Darin Petkov209e6292012-04-20 11:33:32 +020020using std::map;
Darin Petkov7476a262012-04-12 16:30:46 +020021using std::string;
Darin Petkovf7ef50a2012-04-16 20:54:31 +020022using std::vector;
Darin Petkov7476a262012-04-12 16:30:46 +020023
24namespace shill {
25
Darin Petkovf7ef50a2012-04-16 20:54:31 +020026namespace {
27const char kL2TPIPSecIPSecTimeoutProperty[] = "L2TPIPsec.IPsecTimeout";
28const char kL2TPIPSecLeftProtoPortProperty[] = "L2TPIPsec.LeftProtoPort";
29const char kL2TPIPSecLengthBitProperty[] = "L2TPIPsec.LengthBit";
30const char kL2TPIPSecPFSProperty[] = "L2TPIPsec.PFS";
31const char kL2TPIPSecRefusePapProperty[] = "L2TPIPsec.RefusePap";
32const char kL2TPIPSecRekeyProperty[] = "L2TPIPsec.Rekey";
33const char kL2TPIPSecRequireAuthProperty[] = "L2TPIPsec.RequireAuth";
34const char kL2TPIPSecRequireChapProperty[] = "L2TPIPsec.RequireChap";
35const char kL2TPIPSecRightProtoPortProperty[] = "L2TPIPsec.RightProtoPort";
36} // namespace
37
38// static
39const char L2TPIPSecDriver::kPPPDPlugin[] = SCRIPTDIR "/libppp-plugin.so";
Darin Petkovd4325392012-04-23 15:48:22 +020040// static
Darin Petkov209e6292012-04-20 11:33:32 +020041const char L2TPIPSecDriver::kL2TPIPSecVPNPath[] = "/usr/sbin/l2tpipsec_vpn";
Darin Petkovd4325392012-04-23 15:48:22 +020042// static
43const VPNDriver::Property L2TPIPSecDriver::kProperties[] = {
44 { flimflam::kL2tpIpsecCaCertNssProperty, 0 },
45 { flimflam::kL2tpIpsecClientCertIdProperty, 0 },
46 { flimflam::kL2tpIpsecClientCertSlotProperty, 0 },
Darin Petkovcb715292012-04-25 13:04:37 +020047 { flimflam::kL2tpIpsecPasswordProperty, Property::kCredential },
48 { flimflam::kL2tpIpsecPinProperty, Property::kCredential },
49 { flimflam::kL2tpIpsecPskProperty, Property::kCredential },
Darin Petkovd4325392012-04-23 15:48:22 +020050 { flimflam::kL2tpIpsecUserProperty, 0 },
Darin Petkov2c773c22012-04-26 12:54:11 +020051 { flimflam::kProviderHostProperty, 0 },
52 { flimflam::kProviderNameProperty, 0 },
53 { flimflam::kProviderTypeProperty, 0 },
Darin Petkovd4325392012-04-23 15:48:22 +020054 { kL2TPIPSecIPSecTimeoutProperty, 0 },
55 { kL2TPIPSecLeftProtoPortProperty, 0 },
56 { kL2TPIPSecLengthBitProperty, 0 },
57 { kL2TPIPSecPFSProperty, 0 },
58 { kL2TPIPSecRefusePapProperty, 0 },
59 { kL2TPIPSecRekeyProperty, 0 },
60 { kL2TPIPSecRequireAuthProperty, 0 },
61 { kL2TPIPSecRequireChapProperty, 0 },
62 { kL2TPIPSecRightProtoPortProperty, 0 },
63};
Darin Petkovf7ef50a2012-04-16 20:54:31 +020064
Darin Petkov209e6292012-04-20 11:33:32 +020065L2TPIPSecDriver::L2TPIPSecDriver(ControlInterface *control,
Darin Petkovf8046b82012-04-24 16:29:23 +020066 EventDispatcher *dispatcher,
67 Metrics *metrics,
Darin Petkov209e6292012-04-20 11:33:32 +020068 Manager *manager,
Darin Petkovf8046b82012-04-24 16:29:23 +020069 DeviceInfo *device_info,
Darin Petkov209e6292012-04-20 11:33:32 +020070 GLib *glib)
Darin Petkov0e9735d2012-04-24 12:33:45 +020071 : VPNDriver(manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +020072 control_(control),
Darin Petkovf8046b82012-04-24 16:29:23 +020073 dispatcher_(dispatcher),
74 metrics_(metrics),
75 device_info_(device_info),
Darin Petkov209e6292012-04-20 11:33:32 +020076 glib_(glib),
77 nss_(NSS::GetInstance()),
78 pid_(0),
79 child_watch_tag_(0) {}
Darin Petkov7476a262012-04-12 16:30:46 +020080
Darin Petkov209e6292012-04-20 11:33:32 +020081L2TPIPSecDriver::~L2TPIPSecDriver() {
82 Cleanup(Service::kStateIdle);
83}
Darin Petkov7476a262012-04-12 16:30:46 +020084
85bool L2TPIPSecDriver::ClaimInterface(const string &link_name,
86 int interface_index) {
87 // TODO(petkov): crosbug.com/26843.
88 NOTIMPLEMENTED();
89 return false;
90}
91
92void L2TPIPSecDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov209e6292012-04-20 11:33:32 +020093 service_ = service;
94 service_->SetState(Service::kStateConfiguring);
95 rpc_task_.reset(new RPCTask(control_, this));
96 if (!SpawnL2TPIPSecVPN(error)) {
97 Cleanup(Service::kStateFailure);
98 }
Darin Petkov7476a262012-04-12 16:30:46 +020099}
100
101void L2TPIPSecDriver::Disconnect() {
Darin Petkova0e645e2012-04-25 11:38:59 +0200102 SLOG(VPN, 2) << __func__;
103 Cleanup(Service::kStateIdle);
Darin Petkov7476a262012-04-12 16:30:46 +0200104}
105
Darin Petkov5eb05422012-05-11 15:45:25 +0200106void L2TPIPSecDriver::OnConnectionDisconnected() {
107 SLOG(VPN, 2) << __func__;
108 Cleanup(Service::kStateFailure);
109}
110
Darin Petkov7476a262012-04-12 16:30:46 +0200111string L2TPIPSecDriver::GetProviderType() const {
112 return flimflam::kProviderL2tpIpsec;
113}
114
Darin Petkov209e6292012-04-20 11:33:32 +0200115void L2TPIPSecDriver::Cleanup(Service::ConnectState state) {
116 SLOG(VPN, 2) << __func__
117 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov0e9735d2012-04-24 12:33:45 +0200118 DeletePSKFile();
Darin Petkov209e6292012-04-20 11:33:32 +0200119 if (child_watch_tag_) {
120 glib_->SourceRemove(child_watch_tag_);
121 child_watch_tag_ = 0;
122 CHECK(pid_);
123 kill(pid_, SIGTERM);
124 }
125 if (pid_) {
126 glib_->SpawnClosePID(pid_);
127 pid_ = 0;
128 }
Darin Petkovf8046b82012-04-24 16:29:23 +0200129 if (device_) {
130 device_->OnDisconnected();
131 device_->SetEnabled(false);
132 device_ = NULL;
133 }
Darin Petkov209e6292012-04-20 11:33:32 +0200134 rpc_task_.reset();
135 if (service_) {
136 service_->SetState(state);
137 service_ = NULL;
138 }
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200139}
140
Darin Petkov0e9735d2012-04-24 12:33:45 +0200141void L2TPIPSecDriver::DeletePSKFile() {
142 if (!psk_file_.empty()) {
143 file_util::Delete(psk_file_, false);
144 psk_file_.clear();
145 }
146}
147
Darin Petkov209e6292012-04-20 11:33:32 +0200148bool L2TPIPSecDriver::SpawnL2TPIPSecVPN(Error *error) {
149 SLOG(VPN, 2) << __func__;
150
151 vector<string> options;
152 if (!InitOptions(&options, error)) {
153 return false;
154 }
155 SLOG(VPN, 2) << "L2TP/IPSec VPN process options: "
156 << JoinString(options, ' ');
157
158 // TODO(petkov): This code needs to be abstracted away in a separate external
159 // process module (crosbug.com/27131).
160 vector<char *> process_args;
161 process_args.push_back(const_cast<char *>(kL2TPIPSecVPNPath));
162 for (vector<string>::const_iterator it = options.begin();
163 it != options.end(); ++it) {
164 process_args.push_back(const_cast<char *>(it->c_str()));
165 }
166 process_args.push_back(NULL);
167
168 vector<string> environment;
169 InitEnvironment(&environment);
170
171 vector<char *> process_env;
172 for (vector<string>::const_iterator it = environment.begin();
173 it != environment.end(); ++it) {
174 process_env.push_back(const_cast<char *>(it->c_str()));
175 }
176 process_env.push_back(NULL);
177
178 CHECK(!pid_);
179 // Redirect all l2tp/ipsec output to stderr.
180 int stderr_fd = fileno(stderr);
181 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
182 process_env.data(),
183 G_SPAWN_DO_NOT_REAP_CHILD,
184 NULL,
185 NULL,
186 &pid_,
187 NULL,
188 &stderr_fd,
189 &stderr_fd,
190 NULL)) {
191 Error::PopulateAndLog(error, Error::kInternalError,
192 string("Unable to spawn: ") + process_args[0]);
193 return false;
194 }
195 CHECK(!child_watch_tag_);
196 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnL2TPIPSecVPNDied, this);
197 return true;
198}
199
200void L2TPIPSecDriver::InitEnvironment(vector<string> *environment) {
201 environment->push_back(
202 "CONNMAN_BUSNAME=" + rpc_task_->GetRpcConnectionIdentifier());
203 environment->push_back(
204 "CONNMAN_INTERFACE=" + rpc_task_->GetRpcInterfaceIdentifier());
205 environment->push_back(
206 "CONNMAN_PATH=" + rpc_task_->GetRpcIdentifier());
207}
208
209bool L2TPIPSecDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkov01c66042012-04-26 11:10:45 +0200210 string vpnhost = args()->LookupString(flimflam::kProviderHostProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200211 if (vpnhost.empty()) {
212 Error::PopulateAndLog(
213 error, Error::kInvalidArguments, "VPN host not specified.");
Darin Petkov209e6292012-04-20 11:33:32 +0200214 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200215 }
216
217 if (!InitPSKOptions(options, error)) {
Darin Petkov209e6292012-04-20 11:33:32 +0200218 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200219 }
220
221 options->push_back("--remote_host");
222 options->push_back(vpnhost);
223 options->push_back("--pppd_plugin");
224 options->push_back(kPPPDPlugin);
225 // Disable pppd from configuring IP addresses, routes, DNS.
226 options->push_back("--nosystemconfig");
227
228 InitNSSOptions(options);
229
230 AppendValueOption(flimflam::kL2tpIpsecClientCertIdProperty,
231 "--client_cert_id", options);
232 AppendValueOption(flimflam::kL2tpIpsecClientCertSlotProperty,
233 "--client_cert_slot", options);
234 AppendValueOption(flimflam::kL2tpIpsecPinProperty, "--user_pin", options);
235 AppendValueOption(flimflam::kL2tpIpsecUserProperty, "--user", options);
236 AppendValueOption(kL2TPIPSecIPSecTimeoutProperty, "--ipsec_timeout", options);
237 AppendValueOption(kL2TPIPSecLeftProtoPortProperty,
238 "--leftprotoport", options);
239 AppendFlag(kL2TPIPSecPFSProperty, "--pfs", "--nopfs", options);
240 AppendFlag(kL2TPIPSecRekeyProperty, "--rekey", "--norekey", options);
241 AppendValueOption(kL2TPIPSecRightProtoPortProperty,
242 "--rightprotoport", options);
243 AppendFlag(kL2TPIPSecRequireChapProperty,
244 "--require_chap", "--norequire_chap", options);
245 AppendFlag(kL2TPIPSecRefusePapProperty,
246 "--refuse_pap", "--norefuse_pap", options);
247 AppendFlag(kL2TPIPSecRequireAuthProperty,
248 "--require_authentication", "--norequire_authentication", options);
249 AppendFlag(kL2TPIPSecLengthBitProperty,
250 "--length_bit", "--nolength_bit", options);
Darin Petkov209e6292012-04-20 11:33:32 +0200251 if (SLOG_IS_ON(VPN, 0)) {
252 options->push_back("--debug");
253 }
254 return true;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200255}
256
257bool L2TPIPSecDriver::InitPSKOptions(vector<string> *options, Error *error) {
Darin Petkov01c66042012-04-26 11:10:45 +0200258 string psk = args()->LookupString(flimflam::kL2tpIpsecPskProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200259 if (!psk.empty()) {
260 if (!file_util::CreateTemporaryFileInDir(
Darin Petkov0e9735d2012-04-24 12:33:45 +0200261 manager()->run_path(), &psk_file_) ||
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200262 chmod(psk_file_.value().c_str(), S_IRUSR | S_IWUSR) ||
263 file_util::WriteFile(psk_file_, psk.data(), psk.size()) !=
264 static_cast<int>(psk.size())) {
265 Error::PopulateAndLog(
266 error, Error::kInternalError, "Unable to setup psk file.");
267 return false;
268 }
269 options->push_back("--psk_file");
270 options->push_back(psk_file_.value());
271 }
272 return true;
273}
274
275void L2TPIPSecDriver::InitNSSOptions(vector<string> *options) {
276 string ca_cert =
Darin Petkov01c66042012-04-26 11:10:45 +0200277 args()->LookupString(flimflam::kL2tpIpsecCaCertNssProperty, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200278 if (!ca_cert.empty()) {
Darin Petkov01c66042012-04-26 11:10:45 +0200279 const string &vpnhost = args()->GetString(flimflam::kProviderHostProperty);
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200280 vector<char> id(vpnhost.begin(), vpnhost.end());
281 FilePath certfile = nss_->GetDERCertfile(ca_cert, id);
282 if (certfile.empty()) {
283 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
284 } else {
285 options->push_back("--server_ca_file");
286 options->push_back(certfile.value());
287 }
288 }
289}
290
291bool L2TPIPSecDriver::AppendValueOption(
292 const string &property, const string &option, vector<string> *options) {
Darin Petkov01c66042012-04-26 11:10:45 +0200293 string value = args()->LookupString(property, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200294 if (!value.empty()) {
295 options->push_back(option);
296 options->push_back(value);
297 return true;
298 }
299 return false;
300}
301
302bool L2TPIPSecDriver::AppendFlag(const string &property,
303 const string &true_option,
304 const string &false_option,
305 vector<string> *options) {
Darin Petkov01c66042012-04-26 11:10:45 +0200306 string value = args()->LookupString(property, "");
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200307 if (!value.empty()) {
308 options->push_back(value == "true" ? true_option : false_option);
309 return true;
310 }
311 return false;
312}
313
Darin Petkov209e6292012-04-20 11:33:32 +0200314// static
315void L2TPIPSecDriver::OnL2TPIPSecVPNDied(GPid pid, gint status, gpointer data) {
316 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
317 L2TPIPSecDriver *me = reinterpret_cast<L2TPIPSecDriver *>(data);
318 me->child_watch_tag_ = 0;
319 CHECK_EQ(pid, me->pid_);
320 me->Cleanup(Service::kStateFailure);
321 // TODO(petkov): Figure if we need to restart the connection.
322}
323
324void L2TPIPSecDriver::GetLogin(string *user, string *password) {
325 SLOG(VPN, 2) << __func__;
326 string user_property =
Darin Petkov01c66042012-04-26 11:10:45 +0200327 args()->LookupString(flimflam::kL2tpIpsecUserProperty, "");
Darin Petkov209e6292012-04-20 11:33:32 +0200328 if (user_property.empty()) {
329 LOG(ERROR) << "User not set.";
330 return;
331 }
332 string password_property =
Darin Petkov01c66042012-04-26 11:10:45 +0200333 args()->LookupString(flimflam::kL2tpIpsecPasswordProperty, "");
Darin Petkov209e6292012-04-20 11:33:32 +0200334 if (password_property.empty()) {
335 LOG(ERROR) << "Password not set.";
336 return;
337 }
338 *user = user_property;
339 *password = password_property;
340}
341
Darin Petkov0e9735d2012-04-24 12:33:45 +0200342void L2TPIPSecDriver::ParseIPConfiguration(
343 const map<string, string> &configuration,
344 IPConfig::Properties *properties,
345 string *interface_name) {
346 properties->address_family = IPAddress::kFamilyIPv4;
347 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
348 properties->address_family);
349 for (map<string, string>::const_iterator it = configuration.begin();
350 it != configuration.end(); ++it) {
351 const string &key = it->first;
352 const string &value = it->second;
353 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
354 if (key == "INTERNAL_IP4_ADDRESS") {
355 properties->address = value;
356 } else if (key == "EXTERNAL_IP4_ADDRESS") {
357 properties->peer_address = value;
358 } else if (key == "GATEWAY_ADDRESS") {
359 properties->gateway = value;
360 } else if (key == "DNS1") {
361 properties->dns_servers.insert(properties->dns_servers.begin(), value);
362 } else if (key == "DNS2") {
363 properties->dns_servers.push_back(value);
364 } else if (key == "INTERNAL_IFNAME") {
365 *interface_name = value;
366 } else if (key == "LNS_ADDRESS") {
367 properties->trusted_ip = value;
368 } else {
369 SLOG(VPN, 2) << "Key ignored.";
370 }
371 }
372}
373
Darin Petkov209e6292012-04-20 11:33:32 +0200374void L2TPIPSecDriver::Notify(
375 const string &reason, const map<string, string> &dict) {
Darin Petkov0e9735d2012-04-24 12:33:45 +0200376 SLOG(VPN, 2) << __func__ << "(" << reason << ")";
377
378 if (reason != "connect") {
Darin Petkova0e645e2012-04-25 11:38:59 +0200379 device_->OnDisconnected();
Darin Petkov0e9735d2012-04-24 12:33:45 +0200380 return;
381 }
382
Darin Petkovf8046b82012-04-24 16:29:23 +0200383 DeletePSKFile();
384
Darin Petkov0e9735d2012-04-24 12:33:45 +0200385 IPConfig::Properties properties;
386 string interface_name;
387 ParseIPConfiguration(dict, &properties, &interface_name);
Darin Petkovf8046b82012-04-24 16:29:23 +0200388
389 int interface_index = device_info_->GetIndex(interface_name);
390 if (interface_index < 0) {
391 // TODO(petkov): Consider handling the race when the RTNL notification about
392 // the new PPP device has not been received yet. We can keep the IP
393 // configuration and apply it when ClaimInterface is invoked.
394 NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
395 return;
396 }
397
398 if (!device_) {
399 device_ = new VPN(control_, dispatcher_, metrics_, manager(),
400 interface_name, interface_index);
401 }
402 device_->SetEnabled(true);
403 device_->SelectService(service_);
Darin Petkovf8046b82012-04-24 16:29:23 +0200404 device_->UpdateIPConfig(properties);
Darin Petkov209e6292012-04-20 11:33:32 +0200405}
406
Darin Petkovb536a742012-04-26 11:31:28 +0200407KeyValueStore L2TPIPSecDriver::GetProvider(Error *error) {
408 SLOG(VPN, 2) << __func__;
409 KeyValueStore props = VPNDriver::GetProvider(error);
410 props.SetBool(flimflam::kPassphraseRequiredProperty,
411 args()->LookupString(
412 flimflam::kL2tpIpsecPasswordProperty, "").empty());
413 props.SetBool(flimflam::kL2tpIpsecPskRequiredProperty,
414 args()->LookupString(
415 flimflam::kL2tpIpsecPskProperty, "").empty());
416 return props;
417}
418
Darin Petkov7476a262012-04-12 16:30:46 +0200419} // namespace shill