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

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: tweak 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_string(leftprotoport, "17/1701", "client protocol/port");
23 DEFINE_string(rightprotoport, "17/1701", "server protocol/port");
24 DEFINE_string(local, "", "local IP address (needed for PSK)");
25 DEFINE_bool(pfs, false, "pfs");
26 DEFINE_bool(rekey, false, "rekey");
27 #pragma GCC diagnostic error "-Wstrict-aliasing"
28
29 const char kStarterPidFile[] = "/var/run/starter.pid";
30 const char kIpsecConnectionName[] = "ipsec_managed";
31 const char kIpsecUpFile[] = "/tmp/ipsec-up";
32 const char kIpsecGroupName[] = "ipsec";
33 const char kStatefulContainer[] = "/mnt/stateful_partition/etc";
34
35 // Give IPsec layer 2 seconds to shut down before killing it.
36 const int kTermTimeout = 2;
37
38 using ::chromeos::Process;
39 using ::chromeos::ProcessImpl;
40
41 IpsecManager::IpsecManager()
42 : ServiceManager("ipsec"), output_fd_(-1), ike_version_(0),
petkov 2011/03/04 18:42:56 shouldn't the initializer be on separate lines?
kmixter1 2011/03/05 02:48:59 Oops, you're right. Done.
43 ipsec_group_(0), stateful_container_(kStatefulContainer),
44 starter_pid_file_(kStarterPidFile), starter_(new ProcessImpl) {
45 }
46
47 bool IpsecManager::Initialize(int ike_version,
48 const std::string& remote,
49 const std::string& psk_file,
50 const std::string& server_ca_file,
51 const std::string& client_key_file,
52 const std::string& client_cert_file) {
53 if (remote.empty()) {
54 LOG(ERROR) << "Missing remote to IPsec layer";
55 return false;
56 }
57 remote_ = remote;
58
59 if (psk_file.empty() && server_ca_file.empty() && client_key_file.empty() &&
petkov 2011/03/04 18:42:56 move this check under if psk_file.empty() block (j
kmixter1 2011/03/05 02:48:59 Done.
60 client_cert_file.empty()) {
61 LOG(ERROR) << "Must specify either PSK or certificates for IPsec layer";
62 return false;
63 }
64
65 if (psk_file.empty()) {
66 // Must be a certificate based connection.
67 if (!file_util::PathExists(FilePath(server_ca_file))) {
petkov 2011/03/04 18:42:56 server_ca_file could be empty here. maybe you want
kmixter1 2011/03/05 02:48:59 If everything's empty, user will get a message tha
68 LOG(ERROR) << "Invalid server CA file for IPsec layer: "
69 << server_ca_file;
70 return false;
71 }
72 server_ca_file_ = server_ca_file;
73
74 if (!file_util::PathExists(FilePath(client_key_file))) {
75 LOG(ERROR) << "Invalid client key file for IPsec layer: "
76 << client_key_file;
77 return false;
78 }
79 client_key_file_ = client_key_file;
80
81 if (!file_util::PathExists(FilePath(client_cert_file))) {
82 LOG(ERROR) << "Invalid client certificate file for IPsec layer: "
83 << client_key_file;
84 return false;
85 }
86 client_cert_file_ = client_cert_file;
87 } else {
88 if (!server_ca_file.empty() ||
89 !client_key_file.empty() ||
90 !client_cert_file.empty()) {
91 LOG(ERROR) << "Specified both PSK and certificates for IPsec layer";
92 return false;
93 }
94 if (!file_util::PathExists(FilePath(psk_file))) {
95 LOG(ERROR) << "Invalid PSK file for IPsec layer: " << psk_file;
96 return false;
97 }
98 psk_file_ = psk_file;
99 }
100
101 if (ike_version != 1 && ike_version != 2) {
102 LOG(ERROR) << "Unsupported IKE version" << ike_version;
103 return false;
104 }
105 ike_version_ = ike_version;
106
107 file_util::Delete(FilePath(kIpsecUpFile), false);
108
109 return true;
110 }
111
112 bool IpsecManager::FormatPsk(const FilePath& input_file,
113 std::string* formatted) {
114 std::string psk;
115 if (!file_util::ReadFileToString(input_file, &psk)) {
116 LOG(ERROR) << "Unable to read PSK from " << input_file.value();
117 return false;
118 }
119 if (FLAGS_local.empty()) {
120 LOG(ERROR) << "Local IP address must be supplied for PSK mode";
121 return false;
122 }
123 //TODO: use trim function
petkov 2011/03/04 18:42:56 // TODO(kmixter) or fix the todo
kmixter1 2011/03/05 02:48:59 Done.
124 if (psk[psk.size() - 1] == '\n')
125 psk.erase(psk.size() - 1, 1);
126 // TODO(kmixter): Support PSKs with arbitrary characters.
127 *formatted =
128 StringPrintf("%s %s : PSK \"%s\"\n", FLAGS_local.c_str(),
129 remote_.c_str(), psk.c_str());
130 return true;
131 }
132
133 void IpsecManager::KillCurrentlyRunning() {
134 if (!file_util::PathExists(FilePath(starter_pid_file_)))
135 return;
136 starter_->ResetPidByFile(starter_pid_file_);
137 if (Process::ProcessExists(starter_->pid()))
petkov 2011/03/04 18:42:56 you don't just starter_->Reset(0) to avoid ERROR l
kmixter1 2011/03/05 02:48:59 Yes. And those ERROR logs are useful since they in
138 starter_->Reset(0);
139 else
140 starter_->Release();
141 file_util::Delete(FilePath(starter_pid_file_), false);
142 }
143
144 bool IpsecManager::StartStarter() {
145 KillCurrentlyRunning();
146 LOG(INFO) << "Starting starter";
147 pid_t my_pid = getpid();
148 setenv("IPSEC_MANAGER_PID", StringPrintf("%d", my_pid).c_str(), 1);
149 starter_->AddArg(IPSEC_STARTER);
petkov 2011/03/04 18:42:56 where IPSEC_STARTER declared?
kmixter1 2011/03/05 02:48:59 Makefile
150 starter_->AddArg("--nofork");
151 starter_->RedirectUsingPipe(STDERR_FILENO, false);
152 if (!starter_->Start()) {
153 LOG(ERROR) << "Starter did not start successfully";
154 return false;
155 }
156 output_fd_ = starter_->GetPipe(STDERR_FILENO);
157 LOG(INFO) << "Starter started as pid " << starter_->pid();
158 return true;
159 }
160
161 inline void AppendBoolSetting(std::string* config, const char* key,
162 bool value) {
163 config->append(StringPrintf("\t%s=%s\n", key, value ? "yes" : "no"));
164 }
165
166 inline void AppendStringSetting(std::string* config, const char* key,
167 const std::string& value) {
168 config->append(StringPrintf("\t%s=%s\n", key, value.c_str()));
169 }
170
171 inline void AppendIntSetting(std::string* config, const char* key,
172 int value) {
173 config->append(StringPrintf("\t%s=%d\n", key, value));
174 }
175
176 std::string IpsecManager::FormatStarterConfigFile() {
177 std::string config;
178 config.append("config setup\n");
179 if (ike_version_ == 1) {
180 AppendBoolSetting(&config, "charonstart", false);
181 } else {
182 AppendBoolSetting(&config, "plutostart", false);
183 }
184 config.append("conn managed\n");
185 AppendStringSetting(&config, "keyexchange",
186 ike_version_ == 1 ? "ikev1" : "ikev2");
187 if (!psk_file_.empty()) AppendStringSetting(&config, "authby", "psk");
188 AppendBoolSetting(&config, "pfs", FLAGS_pfs);
189 AppendBoolSetting(&config, "rekey", FLAGS_rekey);
190 AppendStringSetting(&config, "left", "%defaultroute");
191 AppendStringSetting(&config, "leftprotoport", FLAGS_leftprotoport);
192 AppendStringSetting(&config, "leftupdown", IPSEC_UPDOWN);
193 AppendStringSetting(&config, "right", remote_);
194 AppendStringSetting(&config, "rightprotoport", FLAGS_rightprotoport);
195 AppendStringSetting(&config, "auto", "start");
196 return config;
197 }
198
199 bool IpsecManager::SetIpsecGroup(const FilePath& file_path) {
200 DLOG(INFO) << "Setting " << file_path.value() << " to gid " << ipsec_group_;
201 return chown(file_path.value().c_str(), getuid(), ipsec_group_) == 0;
202 }
203
204 bool IpsecManager::WriteConfigFiles() {
205 // We need to keep secrets in /mnt/stateful_partition/etc for now
206 // because pluto loses permissions to /home/chronos before it tries
207 // reading secrets. TODO: write this via a fifo.
208 FilePath secrets_path_ = FilePath(stateful_container_).
209 Append("ipsec.secrets");
210 file_util::Delete(secrets_path_, false);
211 if (!psk_file_.empty()) {
212 std::string formatted;
213 if (!FormatPsk(FilePath(psk_file_), &formatted)) {
214 LOG(ERROR) << "Unable to create secrets contents";
215 return false;
216 }
217 if (!file_util::WriteFile(secrets_path_, formatted.c_str(),
218 formatted.length()) ||
219 !SetIpsecGroup(secrets_path_)) {
220 LOG(ERROR) << "Unable to write secrets file " << secrets_path_.value();
221 return false;
222 }
223 } else {
224 LOG(FATAL) << "Certificate mode not yet implemented";
225 }
226 FilePath starter_config_path = temp_path_.Append("ipsec.conf");
227 std::string starter_config = FormatStarterConfigFile();
228 if (!file_util::WriteFile(starter_config_path, starter_config.c_str(),
229 starter_config.size()) ||
230 !SetIpsecGroup(starter_config_path)) {
231 LOG(ERROR) << "Unable to write ipsec config files";
232 return false;
233 }
234 FilePath config_symlink_path = FilePath(stateful_container_).
235 Append("ipsec.conf");
236 file_util::Delete(config_symlink_path, false);
237 if (symlink(starter_config_path.value().c_str(),
238 config_symlink_path.value().c_str()) < 0) {
239 LOG(ERROR) << "Unable to symlink config file";
240 return false;
241 }
242 return true;
243 }
244
245 bool IpsecManager::Start() {
246 if (!ipsec_group_) {
247 struct group group_buffer;
248 struct group* group_result = NULL;
249 char buffer[256];
250 if (getgrnam_r(kIpsecGroupName, &group_buffer, buffer,
251 sizeof(buffer), &group_result) != 0 || !group_result) {
252 LOG(ERROR) << "Cannot find group id for " << kIpsecGroupName;
253 return false;
254 }
255 ipsec_group_ = group_result->gr_gid;
256 LOG(INFO) << "Using ipsec group " << ipsec_group_;
257 }
258 if (!WriteConfigFiles())
259 return false;
260 if (!StartStarter())
261 return false;
262
263 return true;
264 }
265
266 int IpsecManager::Poll() {
267 if (is_running()) return -1;
268 // TODO: run whack/stroke --rereadsecrets instead and use
petkov 2011/03/04 18:42:56 TODO(kmixter)
kmixter1 2011/03/05 02:48:59 Done.
269 // a null up/down script.
270 if (!file_util::PathExists(FilePath(kIpsecUpFile)))
271 return 1000;
272
273 // This indicates that the connection came up successfully.
274 LOG(INFO) << "IPsec connection now up";
275 OnStarted();
276 return -1;
277 }
278
279 void IpsecManager::HandleChild(pid_t pid, int signal) {
280 if (pid != starter_->pid())
281 return;
282
283 LOG(INFO) << "IPsec layer stopped";
284 OnStopped(true);
285 }
286
287 void IpsecManager::Stop() {
288 if (starter_->pid() == 0) {
289 return;
290 }
291
292 if (!starter_->Kill(SIGTERM, kTermTimeout)) {
petkov 2011/03/04 18:42:56 this SIGTERM->SIGKILL sequence seems like a good u
kmixter1 2011/03/05 02:48:59 I agree, but it's complicated. If the process is
293 starter_->Kill(SIGKILL, 0);
294 OnStopped(true);
295 return;
296 }
297 OnStopped(false);
298 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698