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

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: Add line combining 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 <grp.h>
8 #include <sys/types.h>
9 #include <sys/wait.h>
10 #include <unistd.h>
11
12 #include <string>
13 #include <vector>
14
15 #include "base/file_util.h"
16 #include "base/logging.h"
17 #include "base/string_util.h"
18 #include "chromeos/process.h"
19 #include "gflags/gflags.h"
20
21 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
22 DEFINE_int32(ipsec_timeout, 10, "timeout for ipsec to be established");
23 DEFINE_string(local, "", "local IP address (needed for PSK)");
James Simonsen 2011/03/07 20:32:36 local_address would help readability.
kmixter1 2011/03/11 04:48:44 Done.
24 DEFINE_string(leftprotoport, "17/1701", "client protocol/port");
25 DEFINE_bool(pfs, false, "pfs");
26 DEFINE_bool(rekey, false, "rekey");
27 DEFINE_string(rightprotoport, "17/1701", "server protocol/port");
James Simonsen 2011/03/07 20:32:36 Why is this one a command line flag? I don't know
kmixter1 2011/03/11 04:48:44 Ideally every setting could be a command line flag
28 #pragma GCC diagnostic error "-Wstrict-aliasing"
29
30 const char kStarterPidFile[] = "/var/run/starter.pid";
31 const char kIpsecConnectionName[] = "ipsec_managed";
32 const char kIpsecUpFile[] = "/tmp/ipsec-up";
33 const char kIpsecGroupName[] = "ipsec";
34 const char kStatefulContainer[] = "/mnt/stateful_partition/etc";
35
36 // Give IPsec layer 2 seconds to shut down before killing it.
37 const int kTermTimeout = 2;
38
39 using ::chromeos::Process;
40 using ::chromeos::ProcessImpl;
41
42 IpsecManager::IpsecManager()
43 : ServiceManager("ipsec"),
44 output_fd_(-1),
45 ike_version_(0),
46 ipsec_group_(0),
47 stateful_container_(kStatefulContainer),
48 ipsec_up_file_(kIpsecUpFile),
49 starter_pid_file_(kStarterPidFile),
50 starter_(new ProcessImpl) {
51 }
52
53 bool IpsecManager::Initialize(int ike_version,
54 const std::string& remote,
55 const std::string& psk_file,
56 const std::string& server_ca_file,
57 const std::string& client_key_file,
58 const std::string& client_cert_file) {
59 if (remote.empty()) {
James Simonsen 2011/03/07 20:32:36 Remote isn't clear to me. Is it remote server addr
kmixter1 2011/03/11 04:48:44 Done.
60 LOG(ERROR) << "Missing remote to IPsec layer";
61 return false;
62 }
63 remote_ = remote;
64
65 if (psk_file.empty()) {
66 if (server_ca_file.empty() && client_key_file.empty() &&
67 client_cert_file.empty()) {
68 LOG(ERROR) << "Must specify either PSK or certificates for IPsec layer";
69 return false;
70 }
71
72 // Must be a certificate based connection.
73 if (!file_util::PathExists(FilePath(server_ca_file))) {
74 LOG(ERROR) << "Invalid server CA file for IPsec layer: "
75 << server_ca_file;
76 return false;
77 }
78 server_ca_file_ = server_ca_file;
79
80 if (!file_util::PathExists(FilePath(client_key_file))) {
81 LOG(ERROR) << "Invalid client key file for IPsec layer: "
82 << client_key_file;
83 return false;
84 }
85 client_key_file_ = client_key_file;
86
87 if (!file_util::PathExists(FilePath(client_cert_file))) {
88 LOG(ERROR) << "Invalid client certificate file for IPsec layer: "
89 << client_key_file;
90 return false;
91 }
92 client_cert_file_ = client_cert_file;
93 } else {
94 if (!server_ca_file.empty() ||
95 !client_key_file.empty() ||
96 !client_cert_file.empty()) {
97 LOG(ERROR) << "Specified both PSK and certificates for IPsec layer";
98 return false;
99 }
100 if (!file_util::PathExists(FilePath(psk_file))) {
101 LOG(ERROR) << "Invalid PSK file for IPsec layer: " << psk_file;
102 return false;
103 }
104 psk_file_ = psk_file;
105 }
106
107 if (ike_version != 1 && ike_version != 2) {
108 LOG(ERROR) << "Unsupported IKE version" << ike_version;
109 return false;
110 }
111 ike_version_ = ike_version;
112
113 file_util::Delete(FilePath(kIpsecUpFile), false);
114
115 return true;
116 }
117
118 bool IpsecManager::FormatPsk(const FilePath& input_file,
119 std::string* formatted) {
120 std::string psk;
121 if (!file_util::ReadFileToString(input_file, &psk)) {
122 LOG(ERROR) << "Unable to read PSK from " << input_file.value();
123 return false;
124 }
125 if (FLAGS_local.empty()) {
126 LOG(ERROR) << "Local IP address must be supplied for PSK mode";
127 return false;
128 }
129 TrimWhitespaceASCII(psk, TRIM_TRAILING, &psk);
130 *formatted =
131 StringPrintf("%s %s : PSK \"%s\"\n", FLAGS_local.c_str(),
132 remote_.c_str(), psk.c_str());
133 return true;
134 }
135
136 void IpsecManager::KillCurrentlyRunning() {
James Simonsen 2011/03/07 20:32:36 Do you know if just killing the process is safe wh
kmixter1 2011/03/11 04:48:44 I think it's safe. I'm actually completely overri
137 if (!file_util::PathExists(FilePath(starter_pid_file_)))
138 return;
139 starter_->ResetPidByFile(starter_pid_file_);
140 if (Process::ProcessExists(starter_->pid()))
141 starter_->Reset(0);
142 else
143 starter_->Release();
144 file_util::Delete(FilePath(starter_pid_file_), false);
145 }
146
147 bool IpsecManager::StartStarter() {
148 KillCurrentlyRunning();
149 LOG(INFO) << "Starting starter";
150 starter_->AddArg(IPSEC_STARTER);
151 starter_->AddArg("--nofork");
152 starter_->RedirectUsingPipe(STDERR_FILENO, false);
153 if (!starter_->Start()) {
154 LOG(ERROR) << "Starter did not start successfully";
155 return false;
156 }
157 output_fd_ = starter_->GetPipe(STDERR_FILENO);
158 pid_t starter_pid = starter_->pid();
159 LOG(INFO) << "Starter started as pid " << starter_pid;
160 ipsec_prefix_ = StringPrintf("ipsec[%d]: ", starter_pid);
161 return true;
162 }
163
164 inline void AppendBoolSetting(std::string* config, const char* key,
165 bool value) {
166 config->append(StringPrintf("\t%s=%s\n", key, value ? "yes" : "no"));
167 }
168
169 inline void AppendStringSetting(std::string* config, const char* key,
170 const std::string& value) {
171 config->append(StringPrintf("\t%s=%s\n", key, value.c_str()));
172 }
173
174 inline void AppendIntSetting(std::string* config, const char* key,
175 int value) {
176 config->append(StringPrintf("\t%s=%d\n", key, value));
177 }
178
179 std::string IpsecManager::FormatStarterConfigFile() {
180 std::string config;
181 config.append("config setup\n");
182 if (ike_version_ == 1) {
183 AppendBoolSetting(&config, "charonstart", false);
184 } else {
185 AppendBoolSetting(&config, "plutostart", false);
186 }
187 config.append("conn managed\n");
188 AppendStringSetting(&config, "keyexchange",
189 ike_version_ == 1 ? "ikev1" : "ikev2");
190 if (!psk_file_.empty()) AppendStringSetting(&config, "authby", "psk");
191 AppendBoolSetting(&config, "pfs", FLAGS_pfs);
192 AppendBoolSetting(&config, "rekey", FLAGS_rekey);
193 AppendStringSetting(&config, "left", "%defaultroute");
194 AppendStringSetting(&config, "leftprotoport", FLAGS_leftprotoport);
195 AppendStringSetting(&config, "leftupdown", IPSEC_UPDOWN);
196 AppendStringSetting(&config, "right", remote_);
197 AppendStringSetting(&config, "rightprotoport", FLAGS_rightprotoport);
198 AppendStringSetting(&config, "auto", "start");
James Simonsen 2011/03/07 20:32:36 We should probably have rightca too, if we're give
kmixter1 2011/03/11 04:48:44 Same comment - better to hold off on all the ca st
199 return config;
200 }
201
202 bool IpsecManager::SetIpsecGroup(const FilePath& file_path) {
203 return chown(file_path.value().c_str(), getuid(), ipsec_group_) == 0;
204 }
205
206 bool IpsecManager::WriteConfigFiles() {
207 // We need to keep secrets in /mnt/stateful_partition/etc for now
208 // because pluto loses permissions to /home/chronos before it tries
209 // reading secrets.
James Simonsen 2011/03/07 20:32:36 FYI, this affects secrets in the TPM too. It fails
kmixter1 2011/03/11 04:48:44 Yuck.... I hope we don't have to keep pluto as roo
210 // TODO(kmixter): write this via a fifo.
211 FilePath secrets_path_ = FilePath(stateful_container_).
212 Append("ipsec.secrets");
213 file_util::Delete(secrets_path_, false);
214 if (!psk_file_.empty()) {
215 std::string formatted;
216 if (!FormatPsk(FilePath(psk_file_), &formatted)) {
217 LOG(ERROR) << "Unable to create secrets contents";
218 return false;
219 }
220 if (!file_util::WriteFile(secrets_path_, formatted.c_str(),
221 formatted.length()) ||
222 !SetIpsecGroup(secrets_path_)) {
223 LOG(ERROR) << "Unable to write secrets file " << secrets_path_.value();
224 return false;
225 }
226 } else {
227 LOG(FATAL) << "Certificate mode not yet implemented";
James Simonsen 2011/03/07 20:32:36 I can send you my configs if you'd like to add thi
kmixter1 2011/03/11 04:48:44 I'd prefer that one of us does that in a separate
228 }
229 FilePath starter_config_path = temp_path_->Append("ipsec.conf");
230 std::string starter_config = FormatStarterConfigFile();
231 if (!file_util::WriteFile(starter_config_path, starter_config.c_str(),
232 starter_config.size()) ||
233 !SetIpsecGroup(starter_config_path)) {
234 LOG(ERROR) << "Unable to write ipsec config files";
235 return false;
236 }
237 FilePath config_symlink_path = FilePath(stateful_container_).
238 Append("ipsec.conf");
239 file_util::Delete(config_symlink_path, false);
240 if (symlink(starter_config_path.value().c_str(),
241 config_symlink_path.value().c_str()) < 0) {
242 LOG(ERROR) << "Unable to symlink config file";
243 return false;
244 }
245 return true;
246 }
247
248 bool IpsecManager::Start() {
249 if (!ipsec_group_) {
250 struct group group_buffer;
251 struct group* group_result = NULL;
252 char buffer[256];
253 if (getgrnam_r(kIpsecGroupName, &group_buffer, buffer,
254 sizeof(buffer), &group_result) != 0 || !group_result) {
255 LOG(ERROR) << "Cannot find group id for " << kIpsecGroupName;
256 return false;
257 }
258 ipsec_group_ = group_result->gr_gid;
259 DLOG(INFO) << "Using ipsec group " << ipsec_group_;
260 }
261 if (!WriteConfigFiles())
262 return false;
263 if (!StartStarter())
264 return false;
265
266 start_ticks_ = base::TimeTicks::Now();
267
268 return true;
269 }
270
271 int IpsecManager::Poll() {
272 if (is_running()) return -1;
273 if (start_ticks_.is_null()) return -1;
274 if (!file_util::PathExists(FilePath(ipsec_up_file_))) {
275 if (base::TimeTicks::Now() - start_ticks_ >
276 base::TimeDelta::FromSeconds(FLAGS_ipsec_timeout)) {
277 LOG(ERROR) << "IPsec connection timed out";
278 OnStopped(false);
279 // Poll in 1 second in order to check exit conditions.
280 }
281 return 1000;
282 }
283
284 // This indicates that the connection came up successfully.
285 LOG(INFO) << "IPsec connection now up";
286 OnStarted();
287 return -1;
288 }
289
290 void IpsecManager::ProcessOutput() {
291 ServiceManager::WriteFdToSyslog(output_fd_, ipsec_prefix_,
292 &partial_output_line_);
293 }
294
295 bool IpsecManager::IsChild(pid_t pid) {
296 return pid == starter_->pid();
297 }
298
299 void IpsecManager::Stop() {
300 if (starter_->pid() == 0) {
301 return;
302 }
303
304 if (!starter_->Kill(SIGTERM, kTermTimeout)) {
305 starter_->Kill(SIGKILL, 0);
306 OnStopped(true);
307 return;
308 }
309 OnStopped(false);
310 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698