blob: efc3662c9062a12499f8169dbc723253c5bf9353 [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),
45 connected_socket_(-1) {}
Darin Petkov1c115202012-03-22 15:35:47 +010046
Darin Petkov78f63262012-03-26 01:30:24 +020047OpenVPNManagementServer::~OpenVPNManagementServer() {
Darin Petkov46463022012-03-29 14:57:32 +020048 OpenVPNManagementServer::Stop();
Darin Petkov78f63262012-03-26 01:30:24 +020049}
Darin Petkov1c115202012-03-22 15:35:47 +010050
Darin Petkov78f63262012-03-26 01:30:24 +020051bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
Darin Petkov46463022012-03-29 14:57:32 +020052 Sockets *sockets,
53 vector<string> *options) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070054 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +020055 if (IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +020056 return true;
57 }
58
Darin Petkov271fe522012-03-27 13:47:29 +020059 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020060 if (socket < 0) {
61 PLOG(ERROR) << "Unable to create management server socket.";
62 return false;
63 }
64
65 struct sockaddr_in addr;
66 socklen_t addrlen = sizeof(addr);
67 memset(&addr, 0, sizeof(addr));
68 addr.sin_family = AF_INET;
69 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
70 if (sockets->Bind(
71 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
72 sockets->Listen(socket, 1) < 0 ||
73 sockets->GetSockName(
74 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
75 PLOG(ERROR) << "Socket setup failed.";
76 sockets->Close(socket);
77 return false;
78 }
79
Ben Chanfad4a0b2012-04-18 15:49:59 -070080 SLOG(VPN, 2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020081 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020082 socket_ = socket;
83 ready_handler_.reset(
84 dispatcher->CreateReadyHandler(
85 socket, IOHandler::kModeInput, ready_callback_));
86 dispatcher_ = dispatcher;
Darin Petkov46463022012-03-29 14:57:32 +020087
88 // Append openvpn management API options.
89 options->push_back("--management");
90 options->push_back(inet_ntoa(addr.sin_addr));
91 options->push_back(IntToString(ntohs(addr.sin_port)));
92 options->push_back("--management-client");
93 options->push_back("--management-query-passwords");
94 driver_->AppendFlag(
95 flimflam::kOpenVPNAuthUserPassProperty, "--auth-user-pass", options);
96 if (driver_->AppendValueOption(flimflam::kOpenVPNStaticChallengeProperty,
97 "--static-challenge",
98 options)) {
99 options->push_back("1"); // Force echo.
100 }
Darin Petkov78f63262012-03-26 01:30:24 +0200101 return true;
102}
103
104void OpenVPNManagementServer::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700105 SLOG(VPN, 2) << __func__;
Darin Petkove08084d2012-06-11 13:19:35 +0200106 if (!IsStarted()) {
Darin Petkov78f63262012-03-26 01:30:24 +0200107 return;
108 }
109 input_handler_.reset();
110 if (connected_socket_ >= 0) {
111 sockets_->Close(connected_socket_);
112 connected_socket_ = -1;
113 }
114 dispatcher_ = NULL;
115 ready_handler_.reset();
116 if (socket_ >= 0) {
117 sockets_->Close(socket_);
118 socket_ = -1;
119 }
120 sockets_ = NULL;
121}
122
123void OpenVPNManagementServer::OnReady(int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700124 SLOG(VPN, 2) << __func__ << "(" << fd << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200125 connected_socket_ = sockets_->Accept(fd, NULL, NULL);
126 if (connected_socket_ < 0) {
127 PLOG(ERROR) << "Connected socket accept failed.";
128 return;
129 }
130 ready_handler_.reset();
131 input_handler_.reset(
132 dispatcher_->CreateInputHandler(connected_socket_, input_callback_));
133 SendState("on");
134}
135
136void OpenVPNManagementServer::OnInput(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700137 SLOG(VPN, 2) << __func__ << "(" << data->len << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200138 vector<string> messages;
139 SplitString(
140 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
141 for (vector<string>::const_iterator it = messages.begin();
Darin Petkove08084d2012-06-11 13:19:35 +0200142 it != messages.end() && IsStarted(); ++it) {
Darin Petkov78f63262012-03-26 01:30:24 +0200143 ProcessMessage(*it);
144 }
145}
146
147void OpenVPNManagementServer::ProcessMessage(const string &message) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700148 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkov92e65612012-06-10 12:52:10 +0200149 if (message.empty()) {
150 return;
151 }
152 if (!ProcessInfoMessage(message) &&
153 !ProcessNeedPasswordMessage(message) &&
154 !ProcessFailedPasswordMessage(message) &&
155 !ProcessStateMessage(message)) {
156 LOG(WARNING) << "OpenVPN management message ignored: " << message;
157 }
Darin Petkov271fe522012-03-27 13:47:29 +0200158}
159
160bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200161 if (!StartsWithASCII(message, ">INFO:", true)) {
162 return false;
163 }
164 LOG(INFO) << "Processing info message.";
165 return true;
Darin Petkov271fe522012-03-27 13:47:29 +0200166}
167
168bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
169 const string &message) {
170 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
171 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200172 }
Darin Petkov92e65612012-06-10 12:52:10 +0200173 LOG(INFO) << "Processing need-password message.";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200174 string tag = ParseNeedPasswordTag(message);
175 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200176 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200177 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200178 } else {
179 NOTIMPLEMENTED()
Darin Petkov0440b9b2012-04-17 16:11:56 +0200180 << ": User/password (no-OTP) authentication not implemented.";
181 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200182 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200183 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
184 SupplyTPMToken(tag);
Darin Petkov92e65612012-06-10 12:52:10 +0200185 } else {
186 NOTIMPLEMENTED() << ": Unsupported need-password message: " << message;
187 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200188 }
Darin Petkov271fe522012-03-27 13:47:29 +0200189 return true;
190}
191
Darin Petkove0d5dd12012-04-04 16:10:48 +0200192// static
193string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
Darin Petkov92e65612012-06-10 12:52:10 +0200194 SLOG(VPN, 2) << __func__ << "(" << message << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200195 size_t start = message.find('\'');
196 if (start == string::npos) {
197 return string();
198 }
199 size_t end = message.find('\'', start + 1);
200 if (end == string::npos) {
201 return string();
202 }
203 return message.substr(start + 1, end - start - 1);
204}
205
206void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200207 LOG(INFO) << "Perform static challenge: " << tag;
Darin Petkov683942b2012-03-27 18:00:04 +0200208 string user =
209 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
210 string password =
211 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
212 string otp =
213 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
214 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkove08084d2012-06-11 13:19:35 +0200215 NOTIMPLEMENTED() << ": Missing credentials:"
216 << (user.empty() ? " no-user" : "")
217 << (password.empty() ? " no-password" : "")
218 << (otp.empty() ? " no-otp" : "");
Darin Petkov0440b9b2012-04-17 16:11:56 +0200219 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200220 return;
221 }
222 gchar *b64_password =
223 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
224 password.size());
225 gchar *b64_otp =
226 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
227 otp.size());
228 if (!b64_password || !b64_otp) {
229 LOG(ERROR) << "Unable to base64-encode credentials.";
230 return;
231 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200232 SendUsername(tag, user);
233 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200234 glib_->Free(b64_otp);
235 glib_->Free(b64_password);
236 // Don't reuse OTP.
237 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
238}
239
Darin Petkove0d5dd12012-04-04 16:10:48 +0200240void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
Darin Petkov92e65612012-06-10 12:52:10 +0200241 SLOG(VPN, 2) << __func__ << "(" << tag << ")";
Darin Petkove0d5dd12012-04-04 16:10:48 +0200242 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
243 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200244 NOTIMPLEMENTED() << ": Missing PIN.";
245 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200246 return;
247 }
248 SendPassword(tag, pin);
249}
250
Darin Petkov271fe522012-03-27 13:47:29 +0200251bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
252 const string &message) {
253 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
254 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200255 }
Darin Petkov271fe522012-03-27 13:47:29 +0200256 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200257 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200258 return true;
259}
260
261// >STATE:* message support. State messages are of the form:
262// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
263// where:
264// <date> is the current time (since epoch) in seconds
265// <state> is one of:
266// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
267// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
268// <detail> is a free-form string giving details about the state change
269// <local-ip> is a dotted-quad for the local IPv4 address (when available)
270// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
271bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
272 if (!StartsWithASCII(message, ">STATE:", true)) {
273 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200274 }
Darin Petkov92e65612012-06-10 12:52:10 +0200275 LOG(INFO) << "Processing state message.";
Darin Petkov271fe522012-03-27 13:47:29 +0200276 vector<string> details;
277 SplitString(message, ',', &details);
278 if (details.size() > 1) {
279 if (details[1] == "RECONNECTING") {
280 driver_->OnReconnecting();
281 }
282 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200283 }
Darin Petkov271fe522012-03-27 13:47:29 +0200284 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200285}
286
287void OpenVPNManagementServer::Send(const string &data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700288 SLOG(VPN, 2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200289 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
290 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200291 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200292}
293
294void OpenVPNManagementServer::SendState(const string &state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700295 SLOG(VPN, 2) << __func__ << "(" << state << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200296 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100297}
298
Darin Petkov683942b2012-03-27 18:00:04 +0200299void OpenVPNManagementServer::SendUsername(const string &tag,
300 const string &username) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700301 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200302 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
303}
304
305void OpenVPNManagementServer::SendPassword(const string &tag,
306 const string &password) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700307 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200308 Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
309}
310
Darin Petkov1c115202012-03-22 15:35:47 +0100311} // namespace shill