blob: cc0aba4d8a48a9e933ba4bc533b5b7bcb6536ee5 [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 Petkov1c115202012-03-22 15:35:47 +010011#include <base/logging.h>
Darin Petkov46463022012-03-29 14:57:32 +020012#include <base/string_number_conversions.h>
Darin Petkov78f63262012-03-26 01:30:24 +020013#include <base/string_split.h>
14#include <base/string_util.h>
15#include <base/stringprintf.h>
Darin Petkov683942b2012-03-27 18:00:04 +020016#include <chromeos/dbus/service_constants.h>
Darin Petkov78f63262012-03-26 01:30:24 +020017
18#include "shill/event_dispatcher.h"
Darin Petkov683942b2012-03-27 18:00:04 +020019#include "shill/glib.h"
Darin Petkov271fe522012-03-27 13:47:29 +020020#include "shill/openvpn_driver.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070021#include "shill/scope_logger.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;
28using std::string;
29using std::vector;
Darin Petkov1c115202012-03-22 15:35:47 +010030
31namespace shill {
32
Darin Petkov683942b2012-03-27 18:00:04 +020033OpenVPNManagementServer::OpenVPNManagementServer(OpenVPNDriver *driver,
34 GLib *glib)
Darin Petkov78f63262012-03-26 01:30:24 +020035 : driver_(driver),
Darin Petkov683942b2012-03-27 18:00:04 +020036 glib_(glib),
Darin Petkov78f63262012-03-26 01:30:24 +020037 weak_ptr_factory_(this),
38 ready_callback_(Bind(&OpenVPNManagementServer::OnReady,
39 weak_ptr_factory_.GetWeakPtr())),
40 input_callback_(Bind(&OpenVPNManagementServer::OnInput,
41 weak_ptr_factory_.GetWeakPtr())),
42 sockets_(NULL),
43 socket_(-1),
44 dispatcher_(NULL),
Darin Petkova5e07ef2012-07-09 14:27:57 +020045 connected_socket_(-1),
46 hold_waiting_(false),
47 hold_release_(false) {}
Darin Petkov1c115202012-03-22 15:35:47 +010048
Darin Petkov78f63262012-03-26 01:30:24 +020049OpenVPNManagementServer::~OpenVPNManagementServer() {
Darin Petkov46463022012-03-29 14:57:32 +020050 OpenVPNManagementServer::Stop();
Darin Petkov78f63262012-03-26 01:30:24 +020051}
Darin Petkov1c115202012-03-22 15:35:47 +010052
Darin Petkov78f63262012-03-26 01:30:24 +020053bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
Darin Petkov46463022012-03-29 14:57:32 +020054 Sockets *sockets,
55 vector<string> *options) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070056 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +020057 if (IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +020058 return true;
59 }
60
Darin Petkov271fe522012-03-27 13:47:29 +020061 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020062 if (socket < 0) {
63 PLOG(ERROR) << "Unable to create management server socket.";
64 return false;
65 }
66
67 struct sockaddr_in addr;
68 socklen_t addrlen = sizeof(addr);
69 memset(&addr, 0, sizeof(addr));
70 addr.sin_family = AF_INET;
71 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
72 if (sockets->Bind(
73 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
74 sockets->Listen(socket, 1) < 0 ||
75 sockets->GetSockName(
76 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
77 PLOG(ERROR) << "Socket setup failed.";
78 sockets->Close(socket);
79 return false;
80 }
81
Ben Chanfad4a0b2012-04-18 15:49:59 -070082 SLOG(VPN, 2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020083 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020084 socket_ = socket;
85 ready_handler_.reset(
86 dispatcher->CreateReadyHandler(
87 socket, IOHandler::kModeInput, ready_callback_));
88 dispatcher_ = dispatcher;
Darin Petkov46463022012-03-29 14:57:32 +020089
90 // Append openvpn management API options.
91 options->push_back("--management");
92 options->push_back(inet_ntoa(addr.sin_addr));
93 options->push_back(IntToString(ntohs(addr.sin_port)));
94 options->push_back("--management-client");
Darin Petkova5e07ef2012-07-09 14:27:57 +020095
96 options->push_back("--management-hold");
97 hold_release_ = false;
98 hold_waiting_ = false;
99
Darin Petkov46463022012-03-29 14:57:32 +0200100 options->push_back("--management-query-passwords");
101 driver_->AppendFlag(
102 flimflam::kOpenVPNAuthUserPassProperty, "--auth-user-pass", options);
103 if (driver_->AppendValueOption(flimflam::kOpenVPNStaticChallengeProperty,
104 "--static-challenge",
105 options)) {
106 options->push_back("1"); // Force echo.
107 }
Darin Petkov78f63262012-03-26 01:30:24 +0200108 return true;
109}
110
111void OpenVPNManagementServer::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700112 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +0200113 if (!IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +0200114 return;
115 }
116 input_handler_.reset();
117 if (connected_socket_ >= 0) {
118 sockets_->Close(connected_socket_);
119 connected_socket_ = -1;
120 }
121 dispatcher_ = NULL;
122 ready_handler_.reset();
123 if (socket_ >= 0) {
124 sockets_->Close(socket_);
125 socket_ = -1;
126 }
127 sockets_ = NULL;
128}
129
Darin Petkova5e07ef2012-07-09 14:27:57 +0200130void OpenVPNManagementServer::ReleaseHold() {
131 SLOG(VPN, 2) << __func__;
132 hold_release_ = true;
133 if (!hold_waiting_) {
134 return;
135 }
136 LOG(INFO) << "Releasing hold.";
137 hold_waiting_ = false;
138 SendHoldRelease();
139}
140
141void OpenVPNManagementServer::Hold() {
142 SLOG(VPN, 2) << __func__;
143 hold_release_ = false;
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();
154 input_handler_.reset(
155 dispatcher_->CreateInputHandler(connected_socket_, input_callback_));
156 SendState("on");
157}
158
159void OpenVPNManagementServer::OnInput(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700160 SLOG(VPN, 2) << __func__ << "(" << data->len << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200161 vector<string> messages;
162 SplitString(
163 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
164 for (vector<string>::const_iterator it = messages.begin();
Darin Petkove08084d2012-06-11 13:19:35 +0200165 it != messages.end() && IsStarted(); ++it) {
Darin Petkov78f63262012-03-26 01:30:24 +0200166 ProcessMessage(*it);
167 }
168}
169
170void OpenVPNManagementServer::ProcessMessage(const string &message) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700171 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkov92e65612012-06-10 12:52:10 +0200172 if (message.empty()) {
173 return;
174 }
175 if (!ProcessInfoMessage(message) &&
176 !ProcessNeedPasswordMessage(message) &&
177 !ProcessFailedPasswordMessage(message) &&
Darin Petkova5e07ef2012-07-09 14:27:57 +0200178 !ProcessStateMessage(message) &&
179 !ProcessHoldMessage(message)) {
Darin Petkov92e65612012-06-10 12:52:10 +0200180 LOG(WARNING) << "OpenVPN management message ignored: " << message;
181 }
Darin Petkov271fe522012-03-27 13:47:29 +0200182}
183
184bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200185 if (!StartsWithASCII(message, ">INFO:", true)) {
186 return false;
187 }
188 LOG(INFO) << "Processing info message.";
189 return true;
Darin Petkov271fe522012-03-27 13:47:29 +0200190}
191
192bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
193 const string &message) {
194 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
195 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200196 }
Darin Petkov92e65612012-06-10 12:52:10 +0200197 LOG(INFO) << "Processing need-password message.";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200198 string tag = ParseNeedPasswordTag(message);
199 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200200 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200201 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200202 } else {
Darin Petkovdaaa5532012-07-24 15:37:55 +0200203 PerformAuthentication(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200204 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200205 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
206 SupplyTPMToken(tag);
Darin Petkov92e65612012-06-10 12:52:10 +0200207 } else {
208 NOTIMPLEMENTED() << ": Unsupported need-password message: " << message;
209 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200210 }
Darin Petkov271fe522012-03-27 13:47:29 +0200211 return true;
212}
213
Darin Petkove0d5dd12012-04-04 16:10:48 +0200214// static
215string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200216 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200217 size_t start = message.find('\'');
218 if (start == string::npos) {
219 return string();
220 }
221 size_t end = message.find('\'', start + 1);
222 if (end == string::npos) {
223 return string();
224 }
225 return message.substr(start + 1, end - start - 1);
226}
227
228void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200229 LOG(INFO) << "Perform static challenge: " << tag;
Darin Petkov683942b2012-03-27 18:00:04 +0200230 string user =
231 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
232 string password =
233 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
234 string otp =
235 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
236 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkove08084d2012-06-11 13:19:35 +0200237 NOTIMPLEMENTED() << ": Missing credentials:"
238 << (user.empty() ? " no-user" : "")
239 << (password.empty() ? " no-password" : "")
240 << (otp.empty() ? " no-otp" : "");
Darin Petkov0440b9b2012-04-17 16:11:56 +0200241 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200242 return;
243 }
244 gchar *b64_password =
245 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
246 password.size());
247 gchar *b64_otp =
248 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
249 otp.size());
250 if (!b64_password || !b64_otp) {
251 LOG(ERROR) << "Unable to base64-encode credentials.";
252 return;
253 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200254 SendUsername(tag, user);
255 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200256 glib_->Free(b64_otp);
257 glib_->Free(b64_password);
258 // Don't reuse OTP.
259 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
260}
261
Darin Petkovdaaa5532012-07-24 15:37:55 +0200262void OpenVPNManagementServer::PerformAuthentication(const string &tag) {
263 LOG(INFO) << "Perform authentication: " << tag;
264 string user =
265 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
266 string password =
267 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
268 if (user.empty() || password.empty()) {
269 NOTIMPLEMENTED() << ": Missing credentials:"
270 << (user.empty() ? " no-user" : "")
271 << (password.empty() ? " no-password" : "");
272 driver_->Cleanup(Service::kStateFailure);
273 return;
274 }
275 SendUsername(tag, user);
276 SendPassword(tag, password);
277}
278
Darin Petkove0d5dd12012-04-04 16:10:48 +0200279void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200280 SLOG(VPN, 2) << __func__ << "(" << tag << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200281 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
282 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200283 NOTIMPLEMENTED() << ": Missing PIN.";
284 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200285 return;
286 }
287 SendPassword(tag, pin);
288}
289
Darin Petkov271fe522012-03-27 13:47:29 +0200290bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
291 const string &message) {
292 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
293 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200294 }
Darin Petkov271fe522012-03-27 13:47:29 +0200295 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200296 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200297 return true;
298}
299
300// >STATE:* message support. State messages are of the form:
301// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
302// where:
303// <date> is the current time (since epoch) in seconds
304// <state> is one of:
305// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
306// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
307// <detail> is a free-form string giving details about the state change
308// <local-ip> is a dotted-quad for the local IPv4 address (when available)
309// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
310bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
311 if (!StartsWithASCII(message, ">STATE:", true)) {
312 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200313 }
Darin Petkov92e65612012-06-10 12:52:10 +0200314 LOG(INFO) << "Processing state message.";
Darin Petkov271fe522012-03-27 13:47:29 +0200315 vector<string> details;
316 SplitString(message, ',', &details);
317 if (details.size() > 1) {
318 if (details[1] == "RECONNECTING") {
319 driver_->OnReconnecting();
320 }
321 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200322 }
Darin Petkov271fe522012-03-27 13:47:29 +0200323 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200324}
325
Darin Petkova5e07ef2012-07-09 14:27:57 +0200326bool OpenVPNManagementServer::ProcessHoldMessage(const string &message) {
327 if (!StartsWithASCII(message, ">HOLD:Waiting for hold release", true)) {
328 return false;
329 }
330 LOG(INFO) << "Processing hold message.";
331 hold_waiting_ = true;
332 if (hold_release_) {
333 ReleaseHold();
334 }
335 return true;
336}
337
Darin Petkovdaaa5532012-07-24 15:37:55 +0200338// static
339string OpenVPNManagementServer::EscapeToQuote(const string &str) {
340 string escaped;
341 for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
342 if (*it == '\\' || *it == '"') {
343 escaped += '\\';
344 }
345 escaped += *it;
346 }
347 return escaped;
348}
349
Darin Petkov78f63262012-03-26 01:30:24 +0200350void OpenVPNManagementServer::Send(const string &data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700351 SLOG(VPN, 2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200352 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
353 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200354 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200355}
356
357void OpenVPNManagementServer::SendState(const string &state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700358 SLOG(VPN, 2) << __func__ << "(" << state << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200359 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100360}
361
Darin Petkov683942b2012-03-27 18:00:04 +0200362void OpenVPNManagementServer::SendUsername(const string &tag,
363 const string &username) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700364 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200365 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
366}
367
368void OpenVPNManagementServer::SendPassword(const string &tag,
369 const string &password) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700370 SLOG(VPN, 2) << __func__;
Darin Petkovdaaa5532012-07-24 15:37:55 +0200371 Send(StringPrintf("password \"%s\" \"%s\"\n",
372 tag.c_str(),
373 EscapeToQuote(password).c_str()));
Darin Petkov683942b2012-03-27 18:00:04 +0200374}
375
Darin Petkova5e07ef2012-07-09 14:27:57 +0200376void OpenVPNManagementServer::SendHoldRelease() {
377 SLOG(VPN, 2) << __func__;
378 Send("hold release\n");
379}
380
Darin Petkov1c115202012-03-22 15:35:47 +0100381} // namespace shill