blob: 3edbcd7dce238317f7bfe995a357fd1d0d6e7b51 [file] [log] [blame]
Darin Petkov1c115202012-03-22 15:35:47 +01001// 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/openvpn_management_server.h"
6
Darin Petkov46463022012-03-29 14:57:32 +02007#include <arpa/inet.h>
Darin Petkov78f63262012-03-26 01:30:24 +02008#include <netinet/in.h>
9
10#include <base/bind.h>
Darin Petkov46463022012-03-29 14:57:32 +020011#include <base/string_number_conversions.h>
Darin Petkov78f63262012-03-26 01:30:24 +020012#include <base/string_split.h>
13#include <base/string_util.h>
14#include <base/stringprintf.h>
Darin Petkov683942b2012-03-27 18:00:04 +020015#include <chromeos/dbus/service_constants.h>
Darin Petkov78f63262012-03-26 01:30:24 +020016
Darin Petkov3273da72013-02-13 11:50:25 +010017#include "shill/error.h"
Darin Petkov78f63262012-03-26 01:30:24 +020018#include "shill/event_dispatcher.h"
Darin Petkov683942b2012-03-27 18:00:04 +020019#include "shill/glib.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070020#include "shill/logging.h"
Darin Petkov271fe522012-03-27 13:47:29 +020021#include "shill/openvpn_driver.h"
Darin Petkov78f63262012-03-26 01:30:24 +020022#include "shill/sockets.h"
23
24using base::Bind;
Darin Petkov46463022012-03-29 14:57:32 +020025using base::IntToString;
Darin Petkov78f63262012-03-26 01:30:24 +020026using base::SplitString;
27using base::StringPrintf;
Darin Petkov3273da72013-02-13 11:50:25 +010028using base::Unretained;
Darin Petkov78f63262012-03-26 01:30:24 +020029using std::string;
30using std::vector;
Darin Petkov1c115202012-03-22 15:35:47 +010031
32namespace shill {
33
Darin Petkov683942b2012-03-27 18:00:04 +020034OpenVPNManagementServer::OpenVPNManagementServer(OpenVPNDriver *driver,
35 GLib *glib)
Darin Petkov78f63262012-03-26 01:30:24 +020036 : driver_(driver),
Darin Petkov683942b2012-03-27 18:00:04 +020037 glib_(glib),
Darin Petkov78f63262012-03-26 01:30:24 +020038 sockets_(NULL),
39 socket_(-1),
40 dispatcher_(NULL),
Darin Petkova5e07ef2012-07-09 14:27:57 +020041 connected_socket_(-1),
42 hold_waiting_(false),
43 hold_release_(false) {}
Darin Petkov1c115202012-03-22 15:35:47 +010044
Darin Petkov78f63262012-03-26 01:30:24 +020045OpenVPNManagementServer::~OpenVPNManagementServer() {
Darin Petkov46463022012-03-29 14:57:32 +020046 OpenVPNManagementServer::Stop();
Darin Petkov78f63262012-03-26 01:30:24 +020047}
Darin Petkov1c115202012-03-22 15:35:47 +010048
Darin Petkov78f63262012-03-26 01:30:24 +020049bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
Darin Petkov46463022012-03-29 14:57:32 +020050 Sockets *sockets,
51 vector<string> *options) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070052 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +020053 if (IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +020054 return true;
55 }
56
Darin Petkov271fe522012-03-27 13:47:29 +020057 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020058 if (socket < 0) {
59 PLOG(ERROR) << "Unable to create management server socket.";
60 return false;
61 }
62
63 struct sockaddr_in addr;
64 socklen_t addrlen = sizeof(addr);
65 memset(&addr, 0, sizeof(addr));
66 addr.sin_family = AF_INET;
67 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
68 if (sockets->Bind(
69 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
70 sockets->Listen(socket, 1) < 0 ||
71 sockets->GetSockName(
72 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
73 PLOG(ERROR) << "Socket setup failed.";
74 sockets->Close(socket);
75 return false;
76 }
77
Ben Chanfad4a0b2012-04-18 15:49:59 -070078 SLOG(VPN, 2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020079 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020080 socket_ = socket;
81 ready_handler_.reset(
82 dispatcher->CreateReadyHandler(
Darin Petkov3273da72013-02-13 11:50:25 +010083 socket, IOHandler::kModeInput,
84 Bind(&OpenVPNManagementServer::OnReady, Unretained(this))));
Darin Petkov78f63262012-03-26 01:30:24 +020085 dispatcher_ = dispatcher;
Darin Petkov46463022012-03-29 14:57:32 +020086
87 // Append openvpn management API options.
88 options->push_back("--management");
89 options->push_back(inet_ntoa(addr.sin_addr));
90 options->push_back(IntToString(ntohs(addr.sin_port)));
91 options->push_back("--management-client");
Darin Petkova5e07ef2012-07-09 14:27:57 +020092
93 options->push_back("--management-hold");
94 hold_release_ = false;
95 hold_waiting_ = false;
96
Darin Petkov46463022012-03-29 14:57:32 +020097 options->push_back("--management-query-passwords");
Darin Petkov46463022012-03-29 14:57:32 +020098 if (driver_->AppendValueOption(flimflam::kOpenVPNStaticChallengeProperty,
99 "--static-challenge",
100 options)) {
101 options->push_back("1"); // Force echo.
102 }
Darin Petkov78f63262012-03-26 01:30:24 +0200103 return true;
104}
105
106void OpenVPNManagementServer::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700107 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +0200108 if (!IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +0200109 return;
110 }
111 input_handler_.reset();
112 if (connected_socket_ >= 0) {
113 sockets_->Close(connected_socket_);
114 connected_socket_ = -1;
115 }
116 dispatcher_ = NULL;
117 ready_handler_.reset();
118 if (socket_ >= 0) {
119 sockets_->Close(socket_);
120 socket_ = -1;
121 }
122 sockets_ = NULL;
123}
124
Darin Petkova5e07ef2012-07-09 14:27:57 +0200125void OpenVPNManagementServer::ReleaseHold() {
126 SLOG(VPN, 2) << __func__;
127 hold_release_ = true;
128 if (!hold_waiting_) {
129 return;
130 }
131 LOG(INFO) << "Releasing hold.";
132 hold_waiting_ = false;
133 SendHoldRelease();
134}
135
136void OpenVPNManagementServer::Hold() {
137 SLOG(VPN, 2) << __func__;
138 hold_release_ = false;
139}
140
Darin Petkova42afe32013-02-05 16:53:52 +0100141void OpenVPNManagementServer::Restart() {
142 LOG(INFO) << "Restart.";
143 SendSignal("SIGUSR1");
144}
145
Darin Petkov78f63262012-03-26 01:30:24 +0200146void OpenVPNManagementServer::OnReady(int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700147 SLOG(VPN, 2) << __func__ << "(" << fd << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200148 connected_socket_ = sockets_->Accept(fd, NULL, NULL);
149 if (connected_socket_ < 0) {
150 PLOG(ERROR) << "Connected socket accept failed.";
151 return;
152 }
153 ready_handler_.reset();
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800154 input_handler_.reset(dispatcher_->CreateInputHandler(
Darin Petkov3273da72013-02-13 11:50:25 +0100155 connected_socket_,
156 Bind(&OpenVPNManagementServer::OnInput, Unretained(this)),
157 Bind(&OpenVPNManagementServer::OnInputError, Unretained(this))));
Darin Petkov78f63262012-03-26 01:30:24 +0200158 SendState("on");
159}
160
161void OpenVPNManagementServer::OnInput(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700162 SLOG(VPN, 2) << __func__ << "(" << data->len << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200163 vector<string> messages;
164 SplitString(
165 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
166 for (vector<string>::const_iterator it = messages.begin();
Darin Petkove08084d2012-06-11 13:19:35 +0200167 it != messages.end() && IsStarted(); ++it) {
Darin Petkov78f63262012-03-26 01:30:24 +0200168 ProcessMessage(*it);
169 }
170}
171
Darin Petkov3273da72013-02-13 11:50:25 +0100172void OpenVPNManagementServer::OnInputError(const Error &error) {
173 LOG(ERROR) << error;
174 driver_->Cleanup(Service::kStateFailure);
175}
176
Darin Petkov78f63262012-03-26 01:30:24 +0200177void OpenVPNManagementServer::ProcessMessage(const string &message) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700178 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkov92e65612012-06-10 12:52:10 +0200179 if (message.empty()) {
180 return;
181 }
182 if (!ProcessInfoMessage(message) &&
183 !ProcessNeedPasswordMessage(message) &&
184 !ProcessFailedPasswordMessage(message) &&
Darin Petkova5e07ef2012-07-09 14:27:57 +0200185 !ProcessStateMessage(message) &&
Darin Petkova42afe32013-02-05 16:53:52 +0100186 !ProcessHoldMessage(message) &&
187 !ProcessSuccessMessage(message)) {
Darin Petkov92e65612012-06-10 12:52:10 +0200188 LOG(WARNING) << "OpenVPN management message ignored: " << message;
189 }
Darin Petkov271fe522012-03-27 13:47:29 +0200190}
191
192bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200193 if (!StartsWithASCII(message, ">INFO:", true)) {
194 return false;
195 }
Darin Petkova42afe32013-02-05 16:53:52 +0100196 LOG(INFO) << message;
Darin Petkov92e65612012-06-10 12:52:10 +0200197 return true;
Darin Petkov271fe522012-03-27 13:47:29 +0200198}
199
200bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
201 const string &message) {
202 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
203 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200204 }
Darin Petkov92e65612012-06-10 12:52:10 +0200205 LOG(INFO) << "Processing need-password message.";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200206 string tag = ParseNeedPasswordTag(message);
207 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200208 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200209 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200210 } else {
Darin Petkovdaaa5532012-07-24 15:37:55 +0200211 PerformAuthentication(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200212 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200213 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
214 SupplyTPMToken(tag);
Darin Petkov92e65612012-06-10 12:52:10 +0200215 } else {
216 NOTIMPLEMENTED() << ": Unsupported need-password message: " << message;
217 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200218 }
Darin Petkov271fe522012-03-27 13:47:29 +0200219 return true;
220}
221
Darin Petkove0d5dd12012-04-04 16:10:48 +0200222// static
223string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200224 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200225 size_t start = message.find('\'');
226 if (start == string::npos) {
227 return string();
228 }
229 size_t end = message.find('\'', start + 1);
230 if (end == string::npos) {
231 return string();
232 }
233 return message.substr(start + 1, end - start - 1);
234}
235
236void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200237 LOG(INFO) << "Perform static challenge: " << tag;
Darin Petkov683942b2012-03-27 18:00:04 +0200238 string user =
239 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
240 string password =
241 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
242 string otp =
243 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
244 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkove08084d2012-06-11 13:19:35 +0200245 NOTIMPLEMENTED() << ": Missing credentials:"
246 << (user.empty() ? " no-user" : "")
247 << (password.empty() ? " no-password" : "")
248 << (otp.empty() ? " no-otp" : "");
Darin Petkov0440b9b2012-04-17 16:11:56 +0200249 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200250 return;
251 }
252 gchar *b64_password =
253 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
254 password.size());
255 gchar *b64_otp =
256 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
257 otp.size());
258 if (!b64_password || !b64_otp) {
259 LOG(ERROR) << "Unable to base64-encode credentials.";
260 return;
261 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200262 SendUsername(tag, user);
263 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200264 glib_->Free(b64_otp);
265 glib_->Free(b64_password);
266 // Don't reuse OTP.
267 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
268}
269
Darin Petkovdaaa5532012-07-24 15:37:55 +0200270void OpenVPNManagementServer::PerformAuthentication(const string &tag) {
271 LOG(INFO) << "Perform authentication: " << tag;
272 string user =
273 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
274 string password =
275 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
276 if (user.empty() || password.empty()) {
277 NOTIMPLEMENTED() << ": Missing credentials:"
278 << (user.empty() ? " no-user" : "")
279 << (password.empty() ? " no-password" : "");
280 driver_->Cleanup(Service::kStateFailure);
281 return;
282 }
283 SendUsername(tag, user);
284 SendPassword(tag, password);
285}
286
Darin Petkove0d5dd12012-04-04 16:10:48 +0200287void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200288 SLOG(VPN, 2) << __func__ << "(" << tag << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200289 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
290 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200291 NOTIMPLEMENTED() << ": Missing PIN.";
292 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200293 return;
294 }
295 SendPassword(tag, pin);
296}
297
Darin Petkov271fe522012-03-27 13:47:29 +0200298bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
299 const string &message) {
300 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
301 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200302 }
Darin Petkov271fe522012-03-27 13:47:29 +0200303 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200304 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200305 return true;
306}
307
308// >STATE:* message support. State messages are of the form:
309// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
310// where:
311// <date> is the current time (since epoch) in seconds
312// <state> is one of:
313// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
314// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
315// <detail> is a free-form string giving details about the state change
316// <local-ip> is a dotted-quad for the local IPv4 address (when available)
317// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
318bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
319 if (!StartsWithASCII(message, ">STATE:", true)) {
320 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200321 }
Darin Petkov271fe522012-03-27 13:47:29 +0200322 vector<string> details;
323 SplitString(message, ',', &details);
324 if (details.size() > 1) {
325 if (details[1] == "RECONNECTING") {
Darin Petkov0cd0d1e2013-02-11 12:49:10 +0100326 OpenVPNDriver::ReconnectReason reason =
327 OpenVPNDriver::kReconnectReasonUnknown;
328 if (details.size() > 2 && details[2] == "tls-error") {
329 reason = OpenVPNDriver::kReconnectReasonTLSError;
330 }
331 driver_->OnReconnecting(reason);
332 } else {
333 // The rest of the states are currently ignored.
334 LOG(INFO) << "Ignoring state message: " << details[1];
Darin Petkov271fe522012-03-27 13:47:29 +0200335 }
Darin Petkov78f63262012-03-26 01:30:24 +0200336 }
Darin Petkov271fe522012-03-27 13:47:29 +0200337 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200338}
339
Darin Petkova5e07ef2012-07-09 14:27:57 +0200340bool OpenVPNManagementServer::ProcessHoldMessage(const string &message) {
341 if (!StartsWithASCII(message, ">HOLD:Waiting for hold release", true)) {
342 return false;
343 }
Darin Petkova42afe32013-02-05 16:53:52 +0100344 LOG(INFO) << "Client waiting for hold release.";
Darin Petkova5e07ef2012-07-09 14:27:57 +0200345 hold_waiting_ = true;
346 if (hold_release_) {
347 ReleaseHold();
348 }
349 return true;
350}
351
Darin Petkova42afe32013-02-05 16:53:52 +0100352bool OpenVPNManagementServer::ProcessSuccessMessage(const string &message) {
353 if (!StartsWithASCII(message, "SUCCESS: ", true)) {
354 return false;
355 }
356 LOG(INFO) << message;
357 return true;
358}
359
Darin Petkovdaaa5532012-07-24 15:37:55 +0200360// static
361string OpenVPNManagementServer::EscapeToQuote(const string &str) {
362 string escaped;
363 for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
364 if (*it == '\\' || *it == '"') {
365 escaped += '\\';
366 }
367 escaped += *it;
368 }
369 return escaped;
370}
371
Darin Petkov78f63262012-03-26 01:30:24 +0200372void OpenVPNManagementServer::Send(const string &data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700373 SLOG(VPN, 2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200374 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
375 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200376 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200377}
378
379void OpenVPNManagementServer::SendState(const string &state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700380 SLOG(VPN, 2) << __func__ << "(" << state << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200381 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100382}
383
Darin Petkov683942b2012-03-27 18:00:04 +0200384void OpenVPNManagementServer::SendUsername(const string &tag,
385 const string &username) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700386 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200387 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
388}
389
390void OpenVPNManagementServer::SendPassword(const string &tag,
391 const string &password) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700392 SLOG(VPN, 2) << __func__;
Darin Petkovdaaa5532012-07-24 15:37:55 +0200393 Send(StringPrintf("password \"%s\" \"%s\"\n",
394 tag.c_str(),
395 EscapeToQuote(password).c_str()));
Darin Petkov683942b2012-03-27 18:00:04 +0200396}
397
Darin Petkova42afe32013-02-05 16:53:52 +0100398void OpenVPNManagementServer::SendSignal(const string &signal) {
399 SLOG(VPN, 2) << __func__ << "(" << signal << ")";
400 Send(StringPrintf("signal %s\n", signal.c_str()));
401}
402
Darin Petkova5e07ef2012-07-09 14:27:57 +0200403void OpenVPNManagementServer::SendHoldRelease() {
404 SLOG(VPN, 2) << __func__;
405 Send("hold release\n");
406}
407
Darin Petkov1c115202012-03-22 15:35:47 +0100408} // namespace shill