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

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: compute local address instead of taking option 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
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(const std::string& remote_address_te xt,
petkov 2011/03/11 19:12:53 80 chars
kmixter1 2011/03/11 20:53:04 Done.
127 std::string* local_address_text) {
128 static const char kService[] = "80";
129 if (force_local_address_ != NULL) {
130 *local_address_text = force_local_address_;
131 return true;
132 }
133 struct addrinfo *remote_address;
134 int s = getaddrinfo(remote_address_text.c_str(), kService, NULL,
135 &remote_address);
136 if (s != 0) {
137 LOG(ERROR) << "getaddrinfo failed: " << gai_strerror(s);
138 return false;
139 }
140 int sock = HANDLE_EINTR(socket(AF_INET, SOCK_DGRAM, 0));
141 if (sock < 0) {
142 LOG(ERROR) << "Unable to create socket";
143 return false;
144 }
145 if (HANDLE_EINTR(
146 connect(sock, remote_address->ai_addr, sizeof(sockaddr))) != 0) {
147 LOG(ERROR) << "Unable to connect";
148 HANDLE_EINTR(close(sock));
149 return false;
150 }
151 bool result = false;
152 struct sockaddr local_address;
153 socklen_t addr_len = sizeof(local_address);
154 char str[INET6_ADDRSTRLEN] = { 0 };
155 if (getsockname(sock, &local_address, &addr_len) != 0) {
156 int saved_errno = errno;
157 LOG(ERROR) << "getsockname failed on socket connecting to "
158 << remote_address_text << ": " << saved_errno;
159 goto error_label;
petkov 2011/03/11 19:12:53 You could define a ScopedSocketCloser and ScopedAd
kmixter1 2011/03/11 20:53:04 Noted. If I need to write this kind of code again
160 }
161 // convert local_address to local_address_text.
162 switch (local_address.sa_family) {
163 case AF_INET:
164 if (!inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(
165 &local_address)->sin_addr, str, INET6_ADDRSTRLEN)) {
166 LOG(ERROR) << "inet_ntop failed on " << remote_address_text;
167 goto error_label;
168 }
169 break;
170 case AF_INET6:
171 if (!inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6*>(
172 &local_address)->sin6_addr, str, INET6_ADDRSTRLEN)) {
173 LOG(ERROR) << "inet_ntop failed on " << remote_address_text;
174 goto error_label;
175 }
176 break;
177 default:
178 LOG(ERROR) << "Unknown address family converting " << remote_address_text;
179 goto error_label;
180 }
181 *local_address_text = str;
182 LOG(INFO) << "Remote address " << remote_address_text << " has local address "
183 << *local_address_text;
184 result = true;
185
186 error_label:
187 HANDLE_EINTR(close(sock));
188 freeaddrinfo(remote_address);
189 return result;
190 }
191
192 bool IpsecManager::FormatPsk(const FilePath& input_file,
193 std::string* formatted) {
194 std::string psk;
195 if (!file_util::ReadFileToString(input_file, &psk)) {
196 LOG(ERROR) << "Unable to read PSK from " << input_file.value();
197 return false;
198 }
199 std::string local_address;
200 if (!GetLocalAddressForRemote(remote_address_, &local_address)) {
201 LOG(ERROR) << "Local IP address could not be determined for PSK mode";
202 return false;
203 }
204 TrimWhitespaceASCII(psk, TRIM_TRAILING, &psk);
205 *formatted =
206 StringPrintf("%s %s : PSK \"%s\"\n", local_address.c_str(),
207 remote_address_.c_str(), psk.c_str());
208 return true;
209 }
210
211 void IpsecManager::KillCurrentlyRunning() {
212 if (!file_util::PathExists(FilePath(starter_pid_file_)))
213 return;
214 starter_->ResetPidByFile(starter_pid_file_);
215 if (Process::ProcessExists(starter_->pid()))
216 starter_->Reset(0);
217 else
218 starter_->Release();
219 file_util::Delete(FilePath(starter_pid_file_), false);
220 }
221
222 bool IpsecManager::StartStarter() {
223 KillCurrentlyRunning();
224 LOG(INFO) << "Starting starter";
225 starter_->AddArg(IPSEC_STARTER);
226 starter_->AddArg("--nofork");
227 starter_->RedirectUsingPipe(STDERR_FILENO, false);
228 if (!starter_->Start()) {
229 LOG(ERROR) << "Starter did not start successfully";
230 return false;
231 }
232 output_fd_ = starter_->GetPipe(STDERR_FILENO);
233 pid_t starter_pid = starter_->pid();
234 LOG(INFO) << "Starter started as pid " << starter_pid;
235 ipsec_prefix_ = StringPrintf("ipsec[%d]: ", starter_pid);
236 return true;
237 }
238
239 inline void AppendBoolSetting(std::string* config, const char* key,
240 bool value) {
241 config->append(StringPrintf("\t%s=%s\n", key, value ? "yes" : "no"));
242 }
243
244 inline void AppendStringSetting(std::string* config, const char* key,
245 const std::string& value) {
246 config->append(StringPrintf("\t%s=%s\n", key, value.c_str()));
247 }
248
249 inline void AppendIntSetting(std::string* config, const char* key,
250 int value) {
251 config->append(StringPrintf("\t%s=%d\n", key, value));
252 }
253
254 std::string IpsecManager::FormatStarterConfigFile() {
255 std::string config;
256 config.append("config setup\n");
257 if (ike_version_ == 1) {
258 AppendBoolSetting(&config, "charonstart", false);
259 } else {
260 AppendBoolSetting(&config, "plutostart", false);
261 }
262 config.append("conn managed\n");
263 AppendStringSetting(&config, "keyexchange",
264 ike_version_ == 1 ? "ikev1" : "ikev2");
265 if (!psk_file_.empty()) AppendStringSetting(&config, "authby", "psk");
266 AppendBoolSetting(&config, "pfs", FLAGS_pfs);
267 AppendBoolSetting(&config, "rekey", FLAGS_rekey);
268 AppendStringSetting(&config, "left", "%defaultroute");
269 AppendStringSetting(&config, "leftprotoport", FLAGS_leftprotoport);
270 AppendStringSetting(&config, "leftupdown", IPSEC_UPDOWN);
271 AppendStringSetting(&config, "right", remote_address_);
272 AppendStringSetting(&config, "rightprotoport", FLAGS_rightprotoport);
273 AppendStringSetting(&config, "auto", "start");
274 return config;
275 }
276
277 bool IpsecManager::SetIpsecGroup(const FilePath& file_path) {
278 return chown(file_path.value().c_str(), getuid(), ipsec_group_) == 0;
279 }
280
281 bool IpsecManager::WriteConfigFiles() {
282 // We need to keep secrets in /mnt/stateful_partition/etc for now
283 // because pluto loses permissions to /home/chronos before it tries
284 // reading secrets.
285 // TODO(kmixter): write this via a fifo.
286 FilePath secrets_path_ = FilePath(stateful_container_).
287 Append("ipsec.secrets");
288 file_util::Delete(secrets_path_, false);
289 if (!psk_file_.empty()) {
290 std::string formatted;
291 if (!FormatPsk(FilePath(psk_file_), &formatted)) {
292 LOG(ERROR) << "Unable to create secrets contents";
293 return false;
294 }
295 if (!file_util::WriteFile(secrets_path_, formatted.c_str(),
296 formatted.length()) ||
297 !SetIpsecGroup(secrets_path_)) {
298 LOG(ERROR) << "Unable to write secrets file " << secrets_path_.value();
299 return false;
300 }
301 } else {
302 LOG(FATAL) << "Certificate mode not yet implemented";
303 }
304 FilePath starter_config_path = temp_path()->Append("ipsec.conf");
305 std::string starter_config = FormatStarterConfigFile();
306 if (!file_util::WriteFile(starter_config_path, starter_config.c_str(),
307 starter_config.size()) ||
308 !SetIpsecGroup(starter_config_path)) {
309 LOG(ERROR) << "Unable to write ipsec config files";
310 return false;
311 }
312 FilePath config_symlink_path = FilePath(stateful_container_).
313 Append("ipsec.conf");
314 // Use unlink to remove the symlink directly since file_util::Delete
315 // cannot delete dangling symlinks.
316 unlink(config_symlink_path.value().c_str());
317 if (file_util::PathExists(config_symlink_path)) {
318 LOG(ERROR) << "Unable to remove existing file "
319 << config_symlink_path.value();
320 return false;
321 }
322 if (symlink(starter_config_path.value().c_str(),
323 config_symlink_path.value().c_str()) < 0) {
324 int saved_errno = errno;
325 LOG(ERROR) << "Unable to symlink config file "
326 << config_symlink_path.value() << " -> "
327 << starter_config_path.value() << ": " << saved_errno;
328 return false;
329 }
330 return true;
331 }
332
333 bool IpsecManager::CreateIpsecRunDirectory() {
334 if (!file_util::CreateDirectory(FilePath(ipsec_run_path_)) ||
335 !SetIpsecGroup(FilePath(ipsec_run_path_)) ||
336 chmod(ipsec_run_path_.c_str(), kIpsecRunPathMode) != 0) {
337 LOG(ERROR) << "Unable to create " << ipsec_run_path_;
338 return false;
339 }
340 return true;
341 }
342
343 bool IpsecManager::Start() {
344 if (!ipsec_group_) {
345 struct group group_buffer;
346 struct group* group_result = NULL;
347 char buffer[256];
348 if (getgrnam_r(kIpsecGroupName, &group_buffer, buffer,
349 sizeof(buffer), &group_result) != 0 || !group_result) {
350 LOG(ERROR) << "Cannot find group id for " << kIpsecGroupName;
351 return false;
352 }
353 ipsec_group_ = group_result->gr_gid;
354 DLOG(INFO) << "Using ipsec group " << ipsec_group_;
355 }
356 if (!WriteConfigFiles())
357 return false;
358 if (!CreateIpsecRunDirectory())
359 return false;
360 if (!StartStarter())
361 return false;
362
363 start_ticks_ = base::TimeTicks::Now();
364
365 return true;
366 }
367
368 int IpsecManager::Poll() {
369 if (is_running()) return -1;
370 if (start_ticks_.is_null()) return -1;
371 if (!file_util::PathExists(FilePath(ipsec_up_file_))) {
372 if (base::TimeTicks::Now() - start_ticks_ >
373 base::TimeDelta::FromSeconds(FLAGS_ipsec_timeout)) {
374 LOG(ERROR) << "IPsec connection timed out";
375 OnStopped(false);
376 // Poll in 1 second in order to check exit conditions.
377 }
378 return 1000;
379 }
380
381 // This indicates that the connection came up successfully.
382 LOG(INFO) << "IPsec connection now up";
383 OnStarted();
384 return -1;
385 }
386
387 void IpsecManager::ProcessOutput() {
388 ServiceManager::WriteFdToSyslog(output_fd_, ipsec_prefix_,
389 &partial_output_line_);
390 }
391
392 bool IpsecManager::IsChild(pid_t pid) {
393 return pid == starter_->pid();
394 }
395
396 void IpsecManager::Stop() {
397 if (starter_->pid() == 0) {
398 return;
399 }
400
401 if (!starter_->Kill(SIGTERM, kTermTimeout)) {
402 starter_->Kill(SIGKILL, 0);
403 OnStopped(true);
404 return;
405 }
406 OnStopped(false);
407 }
OLDNEW
« no previous file with comments | « ipsec_manager.h ('k') | ipsec_manager_test.cc » ('j') | ipsec_manager_test.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698