blob: 93ed0f38bf2c094b8e5eb601523e666d6286701e [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 },
47 { flimflam::kL2tpIpsecPasswordProperty, Property::kCrypted },
48 { flimflam::kL2tpIpsecPinProperty, Property::kEphemeral },
49 { flimflam::kL2tpIpsecPskProperty, Property::kCrypted },
50 { flimflam::kL2tpIpsecUserProperty, 0 },
51 { kL2TPIPSecIPSecTimeoutProperty, 0 },
52 { kL2TPIPSecLeftProtoPortProperty, 0 },
53 { kL2TPIPSecLengthBitProperty, 0 },
54 { kL2TPIPSecPFSProperty, 0 },
55 { kL2TPIPSecRefusePapProperty, 0 },
56 { kL2TPIPSecRekeyProperty, 0 },
57 { kL2TPIPSecRequireAuthProperty, 0 },
58 { kL2TPIPSecRequireChapProperty, 0 },
59 { kL2TPIPSecRightProtoPortProperty, 0 },
60};
Darin Petkovf7ef50a2012-04-16 20:54:31 +020061
Darin Petkov209e6292012-04-20 11:33:32 +020062L2TPIPSecDriver::L2TPIPSecDriver(ControlInterface *control,
Darin Petkovf8046b82012-04-24 16:29:23 +020063 EventDispatcher *dispatcher,
64 Metrics *metrics,
Darin Petkov209e6292012-04-20 11:33:32 +020065 Manager *manager,
Darin Petkovf8046b82012-04-24 16:29:23 +020066 DeviceInfo *device_info,
Darin Petkov209e6292012-04-20 11:33:32 +020067 GLib *glib)
Darin Petkov0e9735d2012-04-24 12:33:45 +020068 : VPNDriver(manager, kProperties, arraysize(kProperties)),
Darin Petkovb451d6e2012-04-23 11:56:41 +020069 control_(control),
Darin Petkovf8046b82012-04-24 16:29:23 +020070 dispatcher_(dispatcher),
71 metrics_(metrics),
72 device_info_(device_info),
Darin Petkov209e6292012-04-20 11:33:32 +020073 glib_(glib),
74 nss_(NSS::GetInstance()),
75 pid_(0),
76 child_watch_tag_(0) {}
Darin Petkov7476a262012-04-12 16:30:46 +020077
Darin Petkov209e6292012-04-20 11:33:32 +020078L2TPIPSecDriver::~L2TPIPSecDriver() {
79 Cleanup(Service::kStateIdle);
80}
Darin Petkov7476a262012-04-12 16:30:46 +020081
82bool L2TPIPSecDriver::ClaimInterface(const string &link_name,
83 int interface_index) {
84 // TODO(petkov): crosbug.com/26843.
85 NOTIMPLEMENTED();
86 return false;
87}
88
89void L2TPIPSecDriver::Connect(const VPNServiceRefPtr &service, Error *error) {
Darin Petkov209e6292012-04-20 11:33:32 +020090 service_ = service;
91 service_->SetState(Service::kStateConfiguring);
92 rpc_task_.reset(new RPCTask(control_, this));
93 if (!SpawnL2TPIPSecVPN(error)) {
94 Cleanup(Service::kStateFailure);
95 }
Darin Petkov7476a262012-04-12 16:30:46 +020096}
97
98void L2TPIPSecDriver::Disconnect() {
99 // TODO(petkov): crosbug.com/29364.
100 NOTIMPLEMENTED();
101}
102
Darin Petkov7476a262012-04-12 16:30:46 +0200103string L2TPIPSecDriver::GetProviderType() const {
104 return flimflam::kProviderL2tpIpsec;
105}
106
Darin Petkov209e6292012-04-20 11:33:32 +0200107void L2TPIPSecDriver::Cleanup(Service::ConnectState state) {
108 SLOG(VPN, 2) << __func__
109 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov0e9735d2012-04-24 12:33:45 +0200110 DeletePSKFile();
Darin Petkov209e6292012-04-20 11:33:32 +0200111 if (child_watch_tag_) {
112 glib_->SourceRemove(child_watch_tag_);
113 child_watch_tag_ = 0;
114 CHECK(pid_);
115 kill(pid_, SIGTERM);
116 }
117 if (pid_) {
118 glib_->SpawnClosePID(pid_);
119 pid_ = 0;
120 }
Darin Petkovf8046b82012-04-24 16:29:23 +0200121 if (device_) {
122 device_->OnDisconnected();
123 device_->SetEnabled(false);
124 device_ = NULL;
125 }
Darin Petkov209e6292012-04-20 11:33:32 +0200126 rpc_task_.reset();
127 if (service_) {
128 service_->SetState(state);
129 service_ = NULL;
130 }
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200131}
132
Darin Petkov0e9735d2012-04-24 12:33:45 +0200133void L2TPIPSecDriver::DeletePSKFile() {
134 if (!psk_file_.empty()) {
135 file_util::Delete(psk_file_, false);
136 psk_file_.clear();
137 }
138}
139
Darin Petkov209e6292012-04-20 11:33:32 +0200140bool L2TPIPSecDriver::SpawnL2TPIPSecVPN(Error *error) {
141 SLOG(VPN, 2) << __func__;
142
143 vector<string> options;
144 if (!InitOptions(&options, error)) {
145 return false;
146 }
147 SLOG(VPN, 2) << "L2TP/IPSec VPN process options: "
148 << JoinString(options, ' ');
149
150 // TODO(petkov): This code needs to be abstracted away in a separate external
151 // process module (crosbug.com/27131).
152 vector<char *> process_args;
153 process_args.push_back(const_cast<char *>(kL2TPIPSecVPNPath));
154 for (vector<string>::const_iterator it = options.begin();
155 it != options.end(); ++it) {
156 process_args.push_back(const_cast<char *>(it->c_str()));
157 }
158 process_args.push_back(NULL);
159
160 vector<string> environment;
161 InitEnvironment(&environment);
162
163 vector<char *> process_env;
164 for (vector<string>::const_iterator it = environment.begin();
165 it != environment.end(); ++it) {
166 process_env.push_back(const_cast<char *>(it->c_str()));
167 }
168 process_env.push_back(NULL);
169
170 CHECK(!pid_);
171 // Redirect all l2tp/ipsec output to stderr.
172 int stderr_fd = fileno(stderr);
173 if (!glib_->SpawnAsyncWithPipesCWD(process_args.data(),
174 process_env.data(),
175 G_SPAWN_DO_NOT_REAP_CHILD,
176 NULL,
177 NULL,
178 &pid_,
179 NULL,
180 &stderr_fd,
181 &stderr_fd,
182 NULL)) {
183 Error::PopulateAndLog(error, Error::kInternalError,
184 string("Unable to spawn: ") + process_args[0]);
185 return false;
186 }
187 CHECK(!child_watch_tag_);
188 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnL2TPIPSecVPNDied, this);
189 return true;
190}
191
192void L2TPIPSecDriver::InitEnvironment(vector<string> *environment) {
193 environment->push_back(
194 "CONNMAN_BUSNAME=" + rpc_task_->GetRpcConnectionIdentifier());
195 environment->push_back(
196 "CONNMAN_INTERFACE=" + rpc_task_->GetRpcInterfaceIdentifier());
197 environment->push_back(
198 "CONNMAN_PATH=" + rpc_task_->GetRpcIdentifier());
199}
200
201bool L2TPIPSecDriver::InitOptions(vector<string> *options, Error *error) {
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200202 string vpnhost = args_.LookupString(flimflam::kProviderHostProperty, "");
203 if (vpnhost.empty()) {
204 Error::PopulateAndLog(
205 error, Error::kInvalidArguments, "VPN host not specified.");
Darin Petkov209e6292012-04-20 11:33:32 +0200206 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200207 }
208
209 if (!InitPSKOptions(options, error)) {
Darin Petkov209e6292012-04-20 11:33:32 +0200210 return false;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200211 }
212
213 options->push_back("--remote_host");
214 options->push_back(vpnhost);
215 options->push_back("--pppd_plugin");
216 options->push_back(kPPPDPlugin);
217 // Disable pppd from configuring IP addresses, routes, DNS.
218 options->push_back("--nosystemconfig");
219
220 InitNSSOptions(options);
221
222 AppendValueOption(flimflam::kL2tpIpsecClientCertIdProperty,
223 "--client_cert_id", options);
224 AppendValueOption(flimflam::kL2tpIpsecClientCertSlotProperty,
225 "--client_cert_slot", options);
226 AppendValueOption(flimflam::kL2tpIpsecPinProperty, "--user_pin", options);
227 AppendValueOption(flimflam::kL2tpIpsecUserProperty, "--user", options);
228 AppendValueOption(kL2TPIPSecIPSecTimeoutProperty, "--ipsec_timeout", options);
229 AppendValueOption(kL2TPIPSecLeftProtoPortProperty,
230 "--leftprotoport", options);
231 AppendFlag(kL2TPIPSecPFSProperty, "--pfs", "--nopfs", options);
232 AppendFlag(kL2TPIPSecRekeyProperty, "--rekey", "--norekey", options);
233 AppendValueOption(kL2TPIPSecRightProtoPortProperty,
234 "--rightprotoport", options);
235 AppendFlag(kL2TPIPSecRequireChapProperty,
236 "--require_chap", "--norequire_chap", options);
237 AppendFlag(kL2TPIPSecRefusePapProperty,
238 "--refuse_pap", "--norefuse_pap", options);
239 AppendFlag(kL2TPIPSecRequireAuthProperty,
240 "--require_authentication", "--norequire_authentication", options);
241 AppendFlag(kL2TPIPSecLengthBitProperty,
242 "--length_bit", "--nolength_bit", options);
Darin Petkov209e6292012-04-20 11:33:32 +0200243 if (SLOG_IS_ON(VPN, 0)) {
244 options->push_back("--debug");
245 }
246 return true;
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200247}
248
249bool L2TPIPSecDriver::InitPSKOptions(vector<string> *options, Error *error) {
250 string psk = args_.LookupString(flimflam::kL2tpIpsecPskProperty, "");
251 if (!psk.empty()) {
252 if (!file_util::CreateTemporaryFileInDir(
Darin Petkov0e9735d2012-04-24 12:33:45 +0200253 manager()->run_path(), &psk_file_) ||
Darin Petkovf7ef50a2012-04-16 20:54:31 +0200254 chmod(psk_file_.value().c_str(), S_IRUSR | S_IWUSR) ||
255 file_util::WriteFile(psk_file_, psk.data(), psk.size()) !=
256 static_cast<int>(psk.size())) {
257 Error::PopulateAndLog(
258 error, Error::kInternalError, "Unable to setup psk file.");
259 return false;
260 }
261 options->push_back("--psk_file");
262 options->push_back(psk_file_.value());
263 }
264 return true;
265}
266
267void L2TPIPSecDriver::InitNSSOptions(vector<string> *options) {
268 string ca_cert =
269 args_.LookupString(flimflam::kL2tpIpsecCaCertNssProperty, "");
270 if (!ca_cert.empty()) {
271 const string &vpnhost = args_.GetString(flimflam::kProviderHostProperty);
272 vector<char> id(vpnhost.begin(), vpnhost.end());
273 FilePath certfile = nss_->GetDERCertfile(ca_cert, id);
274 if (certfile.empty()) {
275 LOG(ERROR) << "Unable to extract certificate: " << ca_cert;
276 } else {
277 options->push_back("--server_ca_file");
278 options->push_back(certfile.value());
279 }
280 }
281}
282
283bool L2TPIPSecDriver::AppendValueOption(
284 const string &property, const string &option, vector<string> *options) {
285 string value = args_.LookupString(property, "");
286 if (!value.empty()) {
287 options->push_back(option);
288 options->push_back(value);
289 return true;
290 }
291 return false;
292}
293
294bool L2TPIPSecDriver::AppendFlag(const string &property,
295 const string &true_option,
296 const string &false_option,
297 vector<string> *options) {
298 string value = args_.LookupString(property, "");
299 if (!value.empty()) {
300 options->push_back(value == "true" ? true_option : false_option);
301 return true;
302 }
303 return false;
304}
305
Darin Petkov209e6292012-04-20 11:33:32 +0200306// static
307void L2TPIPSecDriver::OnL2TPIPSecVPNDied(GPid pid, gint status, gpointer data) {
308 SLOG(VPN, 2) << __func__ << "(" << pid << ", " << status << ")";
309 L2TPIPSecDriver *me = reinterpret_cast<L2TPIPSecDriver *>(data);
310 me->child_watch_tag_ = 0;
311 CHECK_EQ(pid, me->pid_);
312 me->Cleanup(Service::kStateFailure);
313 // TODO(petkov): Figure if we need to restart the connection.
314}
315
316void L2TPIPSecDriver::GetLogin(string *user, string *password) {
317 SLOG(VPN, 2) << __func__;
318 string user_property =
319 args_.LookupString(flimflam::kL2tpIpsecUserProperty, "");
320 if (user_property.empty()) {
321 LOG(ERROR) << "User not set.";
322 return;
323 }
324 string password_property =
325 args_.LookupString(flimflam::kL2tpIpsecPasswordProperty, "");
326 if (password_property.empty()) {
327 LOG(ERROR) << "Password not set.";
328 return;
329 }
330 *user = user_property;
331 *password = password_property;
332}
333
Darin Petkov0e9735d2012-04-24 12:33:45 +0200334void L2TPIPSecDriver::ParseIPConfiguration(
335 const map<string, string> &configuration,
336 IPConfig::Properties *properties,
337 string *interface_name) {
338 properties->address_family = IPAddress::kFamilyIPv4;
339 properties->subnet_prefix = IPAddress::GetMaxPrefixLength(
340 properties->address_family);
341 for (map<string, string>::const_iterator it = configuration.begin();
342 it != configuration.end(); ++it) {
343 const string &key = it->first;
344 const string &value = it->second;
345 SLOG(VPN, 2) << "Processing: " << key << " -> " << value;
346 if (key == "INTERNAL_IP4_ADDRESS") {
347 properties->address = value;
348 } else if (key == "EXTERNAL_IP4_ADDRESS") {
349 properties->peer_address = value;
350 } else if (key == "GATEWAY_ADDRESS") {
351 properties->gateway = value;
352 } else if (key == "DNS1") {
353 properties->dns_servers.insert(properties->dns_servers.begin(), value);
354 } else if (key == "DNS2") {
355 properties->dns_servers.push_back(value);
356 } else if (key == "INTERNAL_IFNAME") {
357 *interface_name = value;
358 } else if (key == "LNS_ADDRESS") {
359 properties->trusted_ip = value;
360 } else {
361 SLOG(VPN, 2) << "Key ignored.";
362 }
363 }
364}
365
Darin Petkov209e6292012-04-20 11:33:32 +0200366void L2TPIPSecDriver::Notify(
367 const string &reason, const map<string, string> &dict) {
Darin Petkov0e9735d2012-04-24 12:33:45 +0200368 SLOG(VPN, 2) << __func__ << "(" << reason << ")";
369
370 if (reason != "connect") {
371 // TODO(petkov): Disconnect the device (crosbug.com/29364).
372 NOTIMPLEMENTED();
373 return;
374 }
375
Darin Petkovf8046b82012-04-24 16:29:23 +0200376 DeletePSKFile();
377
Darin Petkov0e9735d2012-04-24 12:33:45 +0200378 IPConfig::Properties properties;
379 string interface_name;
380 ParseIPConfiguration(dict, &properties, &interface_name);
Darin Petkovf8046b82012-04-24 16:29:23 +0200381
382 int interface_index = device_info_->GetIndex(interface_name);
383 if (interface_index < 0) {
384 // TODO(petkov): Consider handling the race when the RTNL notification about
385 // the new PPP device has not been received yet. We can keep the IP
386 // configuration and apply it when ClaimInterface is invoked.
387 NOTIMPLEMENTED() << ": No device info for " << interface_name << ".";
388 return;
389 }
390
391 if (!device_) {
392 device_ = new VPN(control_, dispatcher_, metrics_, manager(),
393 interface_name, interface_index);
394 }
395 device_->SetEnabled(true);
396 device_->SelectService(service_);
Darin Petkovf8046b82012-04-24 16:29:23 +0200397 device_->UpdateIPConfig(properties);
Darin Petkov209e6292012-04-20 11:33:32 +0200398}
399
Darin Petkov7476a262012-04-12 16:30:46 +0200400} // namespace shill