Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(234)

Side by Side Diff: ipsec_manager.cc

Issue 6508016: vpn-manager: Add l2tp/ipsec vpn manager (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/vpn-manager.git@master
Patch Set: respond to petkov Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « ipsec_manager.h ('k') | ipsec_manager_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "vpn-manager/ipsec_manager.h"
6
7 #include <arpa/inet.h> // for inet_ntop and inet_pton
8 #include <grp.h>
9 #include <netdb.h> // for getaddrinfo
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <unistd.h>
13
14 #include <string>
15 #include <vector>
16
17 #include "base/eintr_wrapper.h"
18 #include "base/file_util.h"
19 #include "base/logging.h"
20 #include "base/string_util.h"
21 #include "chromeos/process.h"
22 #include "gflags/gflags.h"
23
24 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
25 DEFINE_int32(ipsec_timeout, 10, "timeout for ipsec to be established");
26 DEFINE_string(leftprotoport, "17/1701", "client protocol/port");
27 DEFINE_bool(pfs, false, "pfs");
28 DEFINE_bool(rekey, false, "rekey");
29 DEFINE_string(rightprotoport, "17/1701", "server protocol/port");
30 #pragma GCC diagnostic error "-Wstrict-aliasing"
31
32 const char kIpsecConnectionName[] = "ipsec_managed";
33 const char kIpsecGroupName[] = "ipsec";
34 const char kIpsecRunPath[] = "/var/run/ipsec";
35 const char kIpsecUpFile[] = "/var/run/ipsec/up";
36 const char kIpsecServiceName[] = "ipsec";
37 const char kStarterPidFile[] = "/var/run/starter.pid";
38 const mode_t kIpsecRunPathMode = (S_IRUSR | S_IWUSR | S_IXUSR |
39 S_IRGRP | S_IWGRP | S_IXGRP);
40 const char kStatefulContainer[] = "/mnt/stateful_partition/etc";
41
42 // Give IPsec layer 2 seconds to shut down before killing it.
43 const int kTermTimeout = 2;
44
45 using ::chromeos::Process;
46 using ::chromeos::ProcessImpl;
47
48 IpsecManager::IpsecManager()
49 : ServiceManager(kIpsecServiceName),
50 force_local_address_(NULL),
51 output_fd_(-1),
52 ike_version_(0),
53 ipsec_group_(0),
54 stateful_container_(kStatefulContainer),
55 ipsec_run_path_(kIpsecRunPath),
56 ipsec_up_file_(kIpsecUpFile),
57 starter_pid_file_(kStarterPidFile),
58 starter_(new ProcessImpl) {
59 }
60
61 bool IpsecManager::Initialize(int ike_version,
62 const std::string& remote_address,
63 const std::string& psk_file,
64 const std::string& server_ca_file,
65 const std::string& client_key_file,
66 const std::string& client_cert_file) {
67 if (remote_address.empty()) {
68 LOG(ERROR) << "Missing remote address to IPsec layer";
69 return false;
70 }
71 remote_address_ = remote_address;
72
73 if (psk_file.empty()) {
74 if (server_ca_file.empty() && client_key_file.empty() &&
75 client_cert_file.empty()) {
76 LOG(ERROR) << "Must specify either PSK or certificates for IPsec layer";
77 return false;
78 }
79
80 // Must be a certificate based connection.
81 if (!file_util::PathExists(FilePath(server_ca_file))) {
82 LOG(ERROR) << "Invalid server CA file for IPsec layer: "
83 << server_ca_file;
84 return false;
85 }
86 server_ca_file_ = server_ca_file;
87
88 if (!file_util::PathExists(FilePath(client_key_file))) {
89 LOG(ERROR) << "Invalid client key file for IPsec layer: "
90 << client_key_file;
91 return false;
92 }
93 client_key_file_ = client_key_file;
94
95 if (!file_util::PathExists(FilePath(client_cert_file))) {
96 LOG(ERROR) << "Invalid client certificate file for IPsec layer: "
97 << client_key_file;
98 return false;
99 }
100 client_cert_file_ = client_cert_file;
101 } else {
102 if (!server_ca_file.empty() ||
103 !client_key_file.empty() ||
104 !client_cert_file.empty()) {
105 LOG(ERROR) << "Specified both PSK and certificates for IPsec layer";
106 return false;
107 }
108 if (!file_util::PathExists(FilePath(psk_file))) {
109 LOG(ERROR) << "Invalid PSK file for IPsec layer: " << psk_file;
110 return false;
111 }
112 psk_file_ = psk_file;
113 }
114
115 if (ike_version != 1 && ike_version != 2) {
116 LOG(ERROR) << "Unsupported IKE version" << ike_version;
117 return false;
118 }
119 ike_version_ = ike_version;
120
121 file_util::Delete(FilePath(kIpsecUpFile), false);
122
123 return true;
124 }
125
126 bool IpsecManager::GetLocalAddressForRemote(
127 const std::string& remote_address_text,
128 std::string* local_address_text) {
129 static const char kService[] = "80";
130 if (force_local_address_ != NULL) {
131 *local_address_text = force_local_address_;
132 return true;
133 }
134 struct addrinfo *remote_address;
135 int s = getaddrinfo(remote_address_text.c_str(), kService, NULL,
136 &remote_address);
137 if (s != 0) {
138 LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
139 return false;
140 }
141 int sock = HANDLE_EINTR(socket(AF_INET, SOCK_DGRAM, 0));
142 if (sock < 0) {
143 LOG(ERROR) << "Unable to create socket";
144 return false;
145 }
146 if (HANDLE_EINTR(
147 connect(sock, remote_address->ai_addr, sizeof(sockaddr))) != 0) {
148 LOG(ERROR) << "Unable to connect";
149 HANDLE_EINTR(close(sock));
150 return false;
151 }
152 bool result = false;
153 struct sockaddr local_address;
154 socklen_t addr_len = sizeof(local_address);
155 char str[INET6_ADDRSTRLEN] = { 0 };
156 if (getsockname(sock, &local_address, &addr_len) != 0) {
157 int saved_errno = errno;
158 LOG(ERROR) << "getsockname failed on socket connecting to "
159 << remote_address_text << ": " << saved_errno;
160 goto error_label;
161 }
162 // convert local_address to local_address_text.
163 switch (local_address.sa_family) {
164 case AF_INET:
165 if (!inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(
166 &local_address)->sin_addr, str, INET6_ADDRSTRLEN)) {
167 LOG(ERROR) << "inet_ntop failed on " << remote_address_text;
168 goto error_label;
169 }
170 break;
171 case AF_INET6:
172 if (!inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6*>(
173 &local_address)->sin6_addr, str, INET6_ADDRSTRLEN)) {
174 LOG(ERROR) << "inet_ntop failed on " << remote_address_text;
175 goto error_label;
176 }
177 break;
178 default:
179 LOG(ERROR) << "Unknown address family converting " << remote_address_text;
180 goto error_label;
181 }
182 *local_address_text = str;
183 LOG(INFO) << "Remote address " << remote_address_text << " has local address "
184 << *local_address_text;
185 result = true;
186
187 error_label:
188 HANDLE_EINTR(close(sock));
189 freeaddrinfo(remote_address);
190 return result;
191 }
192
193 bool IpsecManager::FormatPsk(const FilePath& input_file,
194 std::string* formatted) {
195 std::string psk;
196 if (!file_util::ReadFileToString(input_file, &psk)) {
197 LOG(ERROR) << "Unable to read PSK from " << input_file.value();
198 return false;
199 }
200 std::string local_address;
201 if (!GetLocalAddressForRemote(remote_address_, &local_address)) {
202 LOG(ERROR) << "Local IP address could not be determined for PSK mode";
203 return false;
204 }
205 TrimWhitespaceASCII(psk, TRIM_TRAILING, &psk);
206 *formatted =
207 StringPrintf("%s %s : PSK \"%s\"\n", local_address.c_str(),
208 remote_address_.c_str(), psk.c_str());
209 return true;
210 }
211
212 void IpsecManager::KillCurrentlyRunning() {
213 if (!file_util::PathExists(FilePath(starter_pid_file_)))
214 return;
215 starter_->ResetPidByFile(starter_pid_file_);
216 if (Process::ProcessExists(starter_->pid()))
217 starter_->Reset(0);
218 else
219 starter_->Release();
220 file_util::Delete(FilePath(starter_pid_file_), false);
221 }
222
223 bool IpsecManager::StartStarter() {
224 KillCurrentlyRunning();
225 LOG(INFO) << "Starting starter";
226 starter_->AddArg(IPSEC_STARTER);
227 starter_->AddArg("--nofork");
228 starter_->RedirectUsingPipe(STDERR_FILENO, false);
229 if (!starter_->Start()) {
230 LOG(ERROR) << "Starter did not start successfully";
231 return false;
232 }
233 output_fd_ = starter_->GetPipe(STDERR_FILENO);
234 pid_t starter_pid = starter_->pid();
235 LOG(INFO) << "Starter started as pid " << starter_pid;
236 ipsec_prefix_ = StringPrintf("ipsec[%d]: ", starter_pid);
237 return true;
238 }
239
240 inline void AppendBoolSetting(std::string* config, const char* key,
241 bool value) {
242 config->append(StringPrintf("\t%s=%s\n", key, value ? "yes" : "no"));
243 }
244
245 inline void AppendStringSetting(std::string* config, const char* key,
246 const std::string& value) {
247 config->append(StringPrintf("\t%s=%s\n", key, value.c_str()));
248 }
249
250 inline void AppendIntSetting(std::string* config, const char* key,
251 int value) {
252 config->append(StringPrintf("\t%s=%d\n", key, value));
253 }
254
255 std::string IpsecManager::FormatStarterConfigFile() {
256 std::string config;
257 config.append("config setup\n");
258 if (ike_version_ == 1) {
259 AppendBoolSetting(&config, "charonstart", false);
260 } else {
261 AppendBoolSetting(&config, "plutostart", false);
262 }
263 config.append("conn managed\n");
264 AppendStringSetting(&config, "keyexchange",
265 ike_version_ == 1 ? "ikev1" : "ikev2");
266 if (!psk_file_.empty()) AppendStringSetting(&config, "authby", "psk");
267 AppendBoolSetting(&config, "pfs", FLAGS_pfs);
268 AppendBoolSetting(&config, "rekey", FLAGS_rekey);
269 AppendStringSetting(&config, "left", "%defaultroute");
270 AppendStringSetting(&config, "leftprotoport", FLAGS_leftprotoport);
271 AppendStringSetting(&config, "leftupdown", IPSEC_UPDOWN);
272 AppendStringSetting(&config, "right", remote_address_);
273 AppendStringSetting(&config, "rightprotoport", FLAGS_rightprotoport);
274 AppendStringSetting(&config, "auto", "start");
275 return config;
276 }
277
278 bool IpsecManager::SetIpsecGroup(const FilePath& file_path) {
279 return chown(file_path.value().c_str(), getuid(), ipsec_group_) == 0;
280 }
281
282 bool IpsecManager::WriteConfigFiles() {
283 // We need to keep secrets in /mnt/stateful_partition/etc for now
284 // because pluto loses permissions to /home/chronos before it tries
285 // reading secrets.
286 // TODO(kmixter): write this via a fifo.
287 FilePath secrets_path_ = FilePath(stateful_container_).
288 Append("ipsec.secrets");
289 file_util::Delete(secrets_path_, false);
290 if (!psk_file_.empty()) {
291 std::string formatted;
292 if (!FormatPsk(FilePath(psk_file_), &formatted)) {
293 LOG(ERROR) << "Unable to create secrets contents";
294 return false;
295 }
296 if (!file_util::WriteFile(secrets_path_, formatted.c_str(),
297 formatted.length()) ||
298 !SetIpsecGroup(secrets_path_)) {
299 LOG(ERROR) << "Unable to write secrets file " << secrets_path_.value();
300 return false;
301 }
302 } else {
303 LOG(FATAL) << "Certificate mode not yet implemented";
304 }
305 FilePath starter_config_path = temp_path()->Append("ipsec.conf");
306 std::string starter_config = FormatStarterConfigFile();
307 if (!file_util::WriteFile(starter_config_path, starter_config.c_str(),
308 starter_config.size()) ||
309 !SetIpsecGroup(starter_config_path)) {
310 LOG(ERROR) << "Unable to write ipsec config files";
311 return false;
312 }
313 FilePath config_symlink_path = FilePath(stateful_container_).
314 Append("ipsec.conf");
315 // Use unlink to remove the symlink directly since file_util::Delete
316 // cannot delete dangling symlinks.
317 unlink(config_symlink_path.value().c_str());
318 if (file_util::PathExists(config_symlink_path)) {
319 LOG(ERROR) << "Unable to remove existing file "
320 << config_symlink_path.value();
321 return false;
322 }
323 if (symlink(starter_config_path.value().c_str(),
324 config_symlink_path.value().c_str()) < 0) {
325 int saved_errno = errno;
326 LOG(ERROR) << "Unable to symlink config file "
327 << config_symlink_path.value() << " -> "
328 << starter_config_path.value() << ": " << saved_errno;
329 return false;
330 }
331 return true;
332 }
333
334 bool IpsecManager::CreateIpsecRunDirectory() {
335 if (!file_util::CreateDirectory(FilePath(ipsec_run_path_)) ||
336 !SetIpsecGroup(FilePath(ipsec_run_path_)) ||
337 chmod(ipsec_run_path_.c_str(), kIpsecRunPathMode) != 0) {
338 LOG(ERROR) << "Unable to create " << ipsec_run_path_;
339 return false;
340 }
341 return true;
342 }
343
344 bool IpsecManager::Start() {
345 if (!ipsec_group_) {
346 struct group group_buffer;
347 struct group* group_result = NULL;
348 char buffer[256];
349 if (getgrnam_r(kIpsecGroupName, &group_buffer, buffer,
350 sizeof(buffer), &group_result) != 0 || !group_result) {
351 LOG(ERROR) << "Cannot find group id for " << kIpsecGroupName;
352 return false;
353 }
354 ipsec_group_ = group_result->gr_gid;
355 DLOG(INFO) << "Using ipsec group " << ipsec_group_;
356 }
357 if (!WriteConfigFiles())
358 return false;
359 if (!CreateIpsecRunDirectory())
360 return false;
361 if (!StartStarter())
362 return false;
363
364 start_ticks_ = base::TimeTicks::Now();
365
366 return true;
367 }
368
369 int IpsecManager::Poll() {
370 if (is_running()) return -1;
371 if (start_ticks_.is_null()) return -1;
372 if (!file_util::PathExists(FilePath(ipsec_up_file_))) {
373 if (base::TimeTicks::Now() - start_ticks_ >
374 base::TimeDelta::FromSeconds(FLAGS_ipsec_timeout)) {
375 LOG(ERROR) << "IPsec connection timed out";
376 OnStopped(false);
377 // Poll in 1 second in order to check exit conditions.
378 }
379 return 1000;
380 }
381
382 // This indicates that the connection came up successfully.
383 LOG(INFO) << "IPsec connection now up";
384 OnStarted();
385 return -1;
386 }
387
388 void IpsecManager::ProcessOutput() {
389 ServiceManager::WriteFdToSyslog(output_fd_, ipsec_prefix_,
390 &partial_output_line_);
391 }
392
393 bool IpsecManager::IsChild(pid_t pid) {
394 return pid == starter_->pid();
395 }
396
397 void IpsecManager::Stop() {
398 if (starter_->pid() == 0) {
399 return;
400 }
401
402 if (!starter_->Kill(SIGTERM, kTermTimeout)) {
403 starter_->Kill(SIGKILL, 0);
404 OnStopped(true);
405 return;
406 }
407 OnStopped(false);
408 }
OLDNEW
« no previous file with comments | « ipsec_manager.h ('k') | ipsec_manager_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698