blob: a352ee590a3f300cf00dbfbb589cafc4892ff70c [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 Petkov78f63262012-03-26 01:30:24 +020055 if (sockets_) {
56 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 Petkov78f63262012-03-26 01:30:24 +0200106 if (!sockets_) {
107 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();
142 it != messages.end(); ++it) {
143 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 Petkov271fe522012-03-27 13:47:29 +0200149 LOG_IF(WARNING,
150 !ProcessInfoMessage(message) &&
151 !ProcessNeedPasswordMessage(message) &&
152 !ProcessFailedPasswordMessage(message) &&
153 !ProcessStateMessage(message))
154 << "OpenVPN management message ignored: " << message;
155}
156
157bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
158 return StartsWithASCII(message, ">INFO:", true);
159}
160
161bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
162 const string &message) {
163 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
164 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200165 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200166 string tag = ParseNeedPasswordTag(message);
167 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200168 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200169 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200170 } else {
171 NOTIMPLEMENTED()
Darin Petkov0440b9b2012-04-17 16:11:56 +0200172 << ": User/password (no-OTP) authentication not implemented.";
173 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200174 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200175 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
176 SupplyTPMToken(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200177 }
Darin Petkov271fe522012-03-27 13:47:29 +0200178 return true;
179}
180
Darin Petkove0d5dd12012-04-04 16:10:48 +0200181// static
182string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
183 size_t start = message.find('\'');
184 if (start == string::npos) {
185 return string();
186 }
187 size_t end = message.find('\'', start + 1);
188 if (end == string::npos) {
189 return string();
190 }
191 return message.substr(start + 1, end - start - 1);
192}
193
194void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov683942b2012-03-27 18:00:04 +0200195 string user =
196 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
197 string password =
198 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
199 string otp =
200 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
201 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200202 NOTIMPLEMENTED() << ": Missing credentials.";
203 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200204 return;
205 }
206 gchar *b64_password =
207 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
208 password.size());
209 gchar *b64_otp =
210 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
211 otp.size());
212 if (!b64_password || !b64_otp) {
213 LOG(ERROR) << "Unable to base64-encode credentials.";
214 return;
215 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200216 SendUsername(tag, user);
217 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200218 glib_->Free(b64_otp);
219 glib_->Free(b64_password);
220 // Don't reuse OTP.
221 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
222}
223
Darin Petkove0d5dd12012-04-04 16:10:48 +0200224void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
225 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
226 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200227 NOTIMPLEMENTED() << ": Missing PIN.";
228 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200229 return;
230 }
231 SendPassword(tag, pin);
232}
233
Darin Petkov271fe522012-03-27 13:47:29 +0200234bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
235 const string &message) {
236 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
237 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200238 }
Darin Petkov271fe522012-03-27 13:47:29 +0200239 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200240 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200241 return true;
242}
243
244// >STATE:* message support. State messages are of the form:
245// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
246// where:
247// <date> is the current time (since epoch) in seconds
248// <state> is one of:
249// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
250// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
251// <detail> is a free-form string giving details about the state change
252// <local-ip> is a dotted-quad for the local IPv4 address (when available)
253// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
254bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
255 if (!StartsWithASCII(message, ">STATE:", true)) {
256 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200257 }
Darin Petkov271fe522012-03-27 13:47:29 +0200258 vector<string> details;
259 SplitString(message, ',', &details);
260 if (details.size() > 1) {
261 if (details[1] == "RECONNECTING") {
262 driver_->OnReconnecting();
263 }
264 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200265 }
Darin Petkov271fe522012-03-27 13:47:29 +0200266 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200267}
268
269void OpenVPNManagementServer::Send(const string &data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700270 SLOG(VPN, 2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200271 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
272 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200273 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200274}
275
276void OpenVPNManagementServer::SendState(const string &state) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700277 SLOG(VPN, 2) << __func__ << "(" << state << ")";
Darin Petkov78f63262012-03-26 01:30:24 +0200278 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100279}
280
Darin Petkov683942b2012-03-27 18:00:04 +0200281void OpenVPNManagementServer::SendUsername(const string &tag,
282 const string &username) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700283 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200284 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
285}
286
287void OpenVPNManagementServer::SendPassword(const string &tag,
288 const string &password) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700289 SLOG(VPN, 2) << __func__;
Darin Petkov683942b2012-03-27 18:00:04 +0200290 Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
291}
292
Darin Petkov1c115202012-03-22 15:35:47 +0100293} // namespace shill