blob: ff1955ae0d7b624c835929074a15d27778a7c9fa [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
17#include "shill/event_dispatcher.h"
Darin Petkov683942b2012-03-27 18:00:04 +020018#include "shill/glib.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070019#include "shill/logging.h"
Darin Petkov271fe522012-03-27 13:47:29 +020020#include "shill/openvpn_driver.h"
Darin Petkov78f63262012-03-26 01:30:24 +020021#include "shill/sockets.h"
22
23using base::Bind;
Darin Petkov46463022012-03-29 14:57:32 +020024using base::IntToString;
Darin Petkov78f63262012-03-26 01:30:24 +020025using base::SplitString;
26using base::StringPrintf;
27using std::string;
28using std::vector;
Darin Petkov1c115202012-03-22 15:35:47 +010029
30namespace shill {
31
Darin Petkov683942b2012-03-27 18:00:04 +020032OpenVPNManagementServer::OpenVPNManagementServer(OpenVPNDriver *driver,
33 GLib *glib)
Darin Petkov78f63262012-03-26 01:30:24 +020034 : driver_(driver),
Darin Petkov683942b2012-03-27 18:00:04 +020035 glib_(glib),
Darin Petkov78f63262012-03-26 01:30:24 +020036 weak_ptr_factory_(this),
37 ready_callback_(Bind(&OpenVPNManagementServer::OnReady,
38 weak_ptr_factory_.GetWeakPtr())),
39 input_callback_(Bind(&OpenVPNManagementServer::OnInput,
40 weak_ptr_factory_.GetWeakPtr())),
41 sockets_(NULL),
42 socket_(-1),
43 dispatcher_(NULL),
Darin Petkova5e07ef2012-07-09 14:27:57 +020044 connected_socket_(-1),
45 hold_waiting_(false),
46 hold_release_(false) {}
Darin Petkov1c115202012-03-22 15:35:47 +010047
Darin Petkov78f63262012-03-26 01:30:24 +020048OpenVPNManagementServer::~OpenVPNManagementServer() {
Darin Petkov46463022012-03-29 14:57:32 +020049 OpenVPNManagementServer::Stop();
Darin Petkov78f63262012-03-26 01:30:24 +020050}
Darin Petkov1c115202012-03-22 15:35:47 +010051
Darin Petkov78f63262012-03-26 01:30:24 +020052bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
Darin Petkov46463022012-03-29 14:57:32 +020053 Sockets *sockets,
54 vector<string> *options) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070055 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +020056 if (IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +020057 return true;
58 }
59
Darin Petkov271fe522012-03-27 13:47:29 +020060 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020061 if (socket < 0) {
62 PLOG(ERROR) << "Unable to create management server socket.";
63 return false;
64 }
65
66 struct sockaddr_in addr;
67 socklen_t addrlen = sizeof(addr);
68 memset(&addr, 0, sizeof(addr));
69 addr.sin_family = AF_INET;
70 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
71 if (sockets->Bind(
72 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
73 sockets->Listen(socket, 1) < 0 ||
74 sockets->GetSockName(
75 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
76 PLOG(ERROR) << "Socket setup failed.";
77 sockets->Close(socket);
78 return false;
79 }
80
Ben Chanfad4a0b2012-04-18 15:49:59 -070081 SLOG(VPN, 2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020082 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020083 socket_ = socket;
84 ready_handler_.reset(
85 dispatcher->CreateReadyHandler(
86 socket, IOHandler::kModeInput, ready_callback_));
87 dispatcher_ = dispatcher;
Darin Petkov46463022012-03-29 14:57:32 +020088
89 // Append openvpn management API options.
90 options->push_back("--management");
91 options->push_back(inet_ntoa(addr.sin_addr));
92 options->push_back(IntToString(ntohs(addr.sin_port)));
93 options->push_back("--management-client");
Darin Petkova5e07ef2012-07-09 14:27:57 +020094
95 options->push_back("--management-hold");
96 hold_release_ = false;
97 hold_waiting_ = false;
98
Darin Petkov46463022012-03-29 14:57:32 +020099 options->push_back("--management-query-passwords");
Darin Petkov46463022012-03-29 14:57:32 +0200100 if (driver_->AppendValueOption(flimflam::kOpenVPNStaticChallengeProperty,
101 "--static-challenge",
102 options)) {
103 options->push_back("1"); // Force echo.
104 }
Darin Petkov78f63262012-03-26 01:30:24 +0200105 return true;
106}
107
108void OpenVPNManagementServer::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700109 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +0200110 if (!IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +0200111 return;
112 }
113 input_handler_.reset();
114 if (connected_socket_ >= 0) {
115 sockets_->Close(connected_socket_);
116 connected_socket_ = -1;
117 }
118 dispatcher_ = NULL;
119 ready_handler_.reset();
120 if (socket_ >= 0) {
121 sockets_->Close(socket_);
122 socket_ = -1;
123 }
124 sockets_ = NULL;
125}
126
Darin Petkova5e07ef2012-07-09 14:27:57 +0200127void OpenVPNManagementServer::ReleaseHold() {
128 SLOG(VPN, 2) << __func__;
129 hold_release_ = true;
130 if (!hold_waiting_) {
131 return;
132 }
133 LOG(INFO) << "Releasing hold.";
134 hold_waiting_ = false;
135 SendHoldRelease();
136}
137
138void OpenVPNManagementServer::Hold() {
139 SLOG(VPN, 2) << __func__;
140 hold_release_ = false;
141}
142
Darin Petkova42afe32013-02-05 16:53:52 +0100143void OpenVPNManagementServer::Restart() {
144 LOG(INFO) << "Restart.";
145 SendSignal("SIGUSR1");
146}
147
Darin Petkov78f63262012-03-26 01:30:24 +0200148void OpenVPNManagementServer::OnReady(int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700149 SLOG(VPN, 2) << __func__ << "(" << fd << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200150 connected_socket_ = sockets_->Accept(fd, NULL, NULL);
151 if (connected_socket_ < 0) {
152 PLOG(ERROR) << "Connected socket accept failed.";
153 return;
154 }
155 ready_handler_.reset();
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800156 // TODO(petkov): Create an error callback. crosbug.com/37427
157 input_handler_.reset(dispatcher_->CreateInputHandler(
158 connected_socket_, input_callback_, IOHandler::ErrorCallback()));
Darin Petkov78f63262012-03-26 01:30:24 +0200159 SendState("on");
160}
161
162void OpenVPNManagementServer::OnInput(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700163 SLOG(VPN, 2) << __func__ << "(" << data->len << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200164 vector<string> messages;
165 SplitString(
166 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
167 for (vector<string>::const_iterator it = messages.begin();
Darin Petkove08084d2012-06-11 13:19:35 +0200168 it != messages.end() && IsStarted(); ++it) {
Darin Petkov78f63262012-03-26 01:30:24 +0200169 ProcessMessage(*it);
170 }
171}
172
173void OpenVPNManagementServer::ProcessMessage(const string &message) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700174 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkov92e65612012-06-10 12:52:10 +0200175 if (message.empty()) {
176 return;
177 }
178 if (!ProcessInfoMessage(message) &&
179 !ProcessNeedPasswordMessage(message) &&
180 !ProcessFailedPasswordMessage(message) &&
Darin Petkova5e07ef2012-07-09 14:27:57 +0200181 !ProcessStateMessage(message) &&
Darin Petkova42afe32013-02-05 16:53:52 +0100182 !ProcessHoldMessage(message) &&
183 !ProcessSuccessMessage(message)) {
Darin Petkov92e65612012-06-10 12:52:10 +0200184 LOG(WARNING) << "OpenVPN management message ignored: " << message;
185 }
Darin Petkov271fe522012-03-27 13:47:29 +0200186}
187
188bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200189 if (!StartsWithASCII(message, ">INFO:", true)) {
190 return false;
191 }
Darin Petkova42afe32013-02-05 16:53:52 +0100192 LOG(INFO) << message;
Darin Petkov92e65612012-06-10 12:52:10 +0200193 return true;
Darin Petkov271fe522012-03-27 13:47:29 +0200194}
195
196bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
197 const string &message) {
198 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
199 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200200 }
Darin Petkov92e65612012-06-10 12:52:10 +0200201 LOG(INFO) << "Processing need-password message.";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200202 string tag = ParseNeedPasswordTag(message);
203 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200204 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200205 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200206 } else {
Darin Petkovdaaa5532012-07-24 15:37:55 +0200207 PerformAuthentication(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200208 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200209 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
210 SupplyTPMToken(tag);
Darin Petkov92e65612012-06-10 12:52:10 +0200211 } else {
212 NOTIMPLEMENTED() << ": Unsupported need-password message: " << message;
213 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200214 }
Darin Petkov271fe522012-03-27 13:47:29 +0200215 return true;
216}
217
Darin Petkove0d5dd12012-04-04 16:10:48 +0200218// static
219string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200220 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200221 size_t start = message.find('\'');
222 if (start == string::npos) {
223 return string();
224 }
225 size_t end = message.find('\'', start + 1);
226 if (end == string::npos) {
227 return string();
228 }
229 return message.substr(start + 1, end - start - 1);
230}
231
232void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200233 LOG(INFO) << "Perform static challenge: " << tag;
Darin Petkov683942b2012-03-27 18:00:04 +0200234 string user =
235 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
236 string password =
237 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
238 string otp =
239 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
240 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkove08084d2012-06-11 13:19:35 +0200241 NOTIMPLEMENTED() << ": Missing credentials:"
242 << (user.empty() ? " no-user" : "")
243 << (password.empty() ? " no-password" : "")
244 << (otp.empty() ? " no-otp" : "");
Darin Petkov0440b9b2012-04-17 16:11:56 +0200245 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200246 return;
247 }
248 gchar *b64_password =
249 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
250 password.size());
251 gchar *b64_otp =
252 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
253 otp.size());
254 if (!b64_password || !b64_otp) {
255 LOG(ERROR) << "Unable to base64-encode credentials.";
256 return;
257 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200258 SendUsername(tag, user);
259 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200260 glib_->Free(b64_otp);
261 glib_->Free(b64_password);
262 // Don't reuse OTP.
263 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
264}
265
Darin Petkovdaaa5532012-07-24 15:37:55 +0200266void OpenVPNManagementServer::PerformAuthentication(const string &tag) {
267 LOG(INFO) << "Perform authentication: " << tag;
268 string user =
269 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
270 string password =
271 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
272 if (user.empty() || password.empty()) {
273 NOTIMPLEMENTED() << ": Missing credentials:"
274 << (user.empty() ? " no-user" : "")
275 << (password.empty() ? " no-password" : "");
276 driver_->Cleanup(Service::kStateFailure);
277 return;
278 }
279 SendUsername(tag, user);
280 SendPassword(tag, password);
281}
282
Darin Petkove0d5dd12012-04-04 16:10:48 +0200283void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200284 SLOG(VPN, 2) << __func__ << "(" << tag << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200285 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
286 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200287 NOTIMPLEMENTED() << ": Missing PIN.";
288 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200289 return;
290 }
291 SendPassword(tag, pin);
292}
293
Darin Petkov271fe522012-03-27 13:47:29 +0200294bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
295 const string &message) {
296 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
297 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200298 }
Darin Petkov271fe522012-03-27 13:47:29 +0200299 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200300 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200301 return true;
302}
303
304// >STATE:* message support. State messages are of the form:
305// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
306// where:
307// <date> is the current time (since epoch) in seconds
308// <state> is one of:
309// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
310// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
311// <detail> is a free-form string giving details about the state change
312// <local-ip> is a dotted-quad for the local IPv4 address (when available)
313// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
314bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
315 if (!StartsWithASCII(message, ">STATE:", true)) {
316 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200317 }
Darin Petkov271fe522012-03-27 13:47:29 +0200318 vector<string> details;
319 SplitString(message, ',', &details);
320 if (details.size() > 1) {
Darin Petkova42afe32013-02-05 16:53:52 +0100321 LOG(INFO) << "Processing state message: " << details[1];
Darin Petkov271fe522012-03-27 13:47:29 +0200322 if (details[1] == "RECONNECTING") {
323 driver_->OnReconnecting();
324 }
325 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200326 }
Darin Petkov271fe522012-03-27 13:47:29 +0200327 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200328}
329
Darin Petkova5e07ef2012-07-09 14:27:57 +0200330bool OpenVPNManagementServer::ProcessHoldMessage(const string &message) {
331 if (!StartsWithASCII(message, ">HOLD:Waiting for hold release", true)) {
332 return false;
333 }
Darin Petkova42afe32013-02-05 16:53:52 +0100334 LOG(INFO) << "Client waiting for hold release.";
Darin Petkova5e07ef2012-07-09 14:27:57 +0200335 hold_waiting_ = true;
336 if (hold_release_) {
337 ReleaseHold();
338 }
339 return true;
340}
341
Darin Petkova42afe32013-02-05 16:53:52 +0100342bool OpenVPNManagementServer::ProcessSuccessMessage(const string &message) {
343 if (!StartsWithASCII(message, "SUCCESS: ", true)) {
344 return false;
345 }
346 LOG(INFO) << message;
347 return true;
348}
349
Darin Petkovdaaa5532012-07-24 15:37:55 +0200350// static
351string OpenVPNManagementServer::EscapeToQuote(const string &str) {
352 string escaped;
353 for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
354 if (*it == '\\' || *it == '"') {
355 escaped += '\\';
356 }
357 escaped += *it;
358 }
359 return escaped;
360}
361
Darin Petkov78f63262012-03-26 01:30:24 +0200362void OpenVPNManagementServer::Send(const string &data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700363 SLOG(VPN, 2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200364 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
365 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200366 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200367}
368
369void OpenVPNManagementServer::SendState(const string &state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700370 SLOG(VPN, 2) << __func__ << "(" << state << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200371 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100372}
373
Darin Petkov683942b2012-03-27 18:00:04 +0200374void OpenVPNManagementServer::SendUsername(const string &tag,
375 const string &username) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700376 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200377 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
378}
379
380void OpenVPNManagementServer::SendPassword(const string &tag,
381 const string &password) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700382 SLOG(VPN, 2) << __func__;
Darin Petkovdaaa5532012-07-24 15:37:55 +0200383 Send(StringPrintf("password \"%s\" \"%s\"\n",
384 tag.c_str(),
385 EscapeToQuote(password).c_str()));
Darin Petkov683942b2012-03-27 18:00:04 +0200386}
387
Darin Petkova42afe32013-02-05 16:53:52 +0100388void OpenVPNManagementServer::SendSignal(const string &signal) {
389 SLOG(VPN, 2) << __func__ << "(" << signal << ")";
390 Send(StringPrintf("signal %s\n", signal.c_str()));
391}
392
Darin Petkova5e07ef2012-07-09 14:27:57 +0200393void OpenVPNManagementServer::SendHoldRelease() {
394 SLOG(VPN, 2) << __func__;
395 Send("hold release\n");
396}
397
Darin Petkov1c115202012-03-22 15:35:47 +0100398} // namespace shill