blob: 7fd42f224abfb5041f0a4379e6d2a42db7ab8d34 [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"
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),
44 connected_socket_(-1) {}
Darin Petkov1c115202012-03-22 15:35:47 +010045
Darin Petkov78f63262012-03-26 01:30:24 +020046OpenVPNManagementServer::~OpenVPNManagementServer() {
Darin Petkov46463022012-03-29 14:57:32 +020047 OpenVPNManagementServer::Stop();
Darin Petkov78f63262012-03-26 01:30:24 +020048}
Darin Petkov1c115202012-03-22 15:35:47 +010049
Darin Petkov78f63262012-03-26 01:30:24 +020050bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
Darin Petkov46463022012-03-29 14:57:32 +020051 Sockets *sockets,
52 vector<string> *options) {
Darin Petkov1c115202012-03-22 15:35:47 +010053 VLOG(2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +020054 if (sockets_) {
55 return true;
56 }
57
Darin Petkov271fe522012-03-27 13:47:29 +020058 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020059 if (socket < 0) {
60 PLOG(ERROR) << "Unable to create management server socket.";
61 return false;
62 }
63
64 struct sockaddr_in addr;
65 socklen_t addrlen = sizeof(addr);
66 memset(&addr, 0, sizeof(addr));
67 addr.sin_family = AF_INET;
68 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
69 if (sockets->Bind(
70 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
71 sockets->Listen(socket, 1) < 0 ||
72 sockets->GetSockName(
73 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
74 PLOG(ERROR) << "Socket setup failed.";
75 sockets->Close(socket);
76 return false;
77 }
78
79 VLOG(2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020080 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020081 socket_ = socket;
82 ready_handler_.reset(
83 dispatcher->CreateReadyHandler(
84 socket, IOHandler::kModeInput, ready_callback_));
85 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");
92 options->push_back("--management-query-passwords");
93 driver_->AppendFlag(
94 flimflam::kOpenVPNAuthUserPassProperty, "--auth-user-pass", options);
95 if (driver_->AppendValueOption(flimflam::kOpenVPNStaticChallengeProperty,
96 "--static-challenge",
97 options)) {
98 options->push_back("1"); // Force echo.
99 }
Darin Petkov78f63262012-03-26 01:30:24 +0200100 return true;
101}
102
103void OpenVPNManagementServer::Stop() {
104 VLOG(2) << __func__;
105 if (!sockets_) {
106 return;
107 }
108 input_handler_.reset();
109 if (connected_socket_ >= 0) {
110 sockets_->Close(connected_socket_);
111 connected_socket_ = -1;
112 }
113 dispatcher_ = NULL;
114 ready_handler_.reset();
115 if (socket_ >= 0) {
116 sockets_->Close(socket_);
117 socket_ = -1;
118 }
119 sockets_ = NULL;
120}
121
122void OpenVPNManagementServer::OnReady(int fd) {
123 VLOG(2) << __func__ << "(" << fd << ")";
124 connected_socket_ = sockets_->Accept(fd, NULL, NULL);
125 if (connected_socket_ < 0) {
126 PLOG(ERROR) << "Connected socket accept failed.";
127 return;
128 }
129 ready_handler_.reset();
130 input_handler_.reset(
131 dispatcher_->CreateInputHandler(connected_socket_, input_callback_));
132 SendState("on");
133}
134
135void OpenVPNManagementServer::OnInput(InputData *data) {
136 VLOG(2) << __func__ << "(" << data->len << ")";
137 vector<string> messages;
138 SplitString(
139 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
140 for (vector<string>::const_iterator it = messages.begin();
141 it != messages.end(); ++it) {
142 ProcessMessage(*it);
143 }
144}
145
146void OpenVPNManagementServer::ProcessMessage(const string &message) {
147 VLOG(2) << __func__ << "(" << message << ")";
Darin Petkov271fe522012-03-27 13:47:29 +0200148 LOG_IF(WARNING,
149 !ProcessInfoMessage(message) &&
150 !ProcessNeedPasswordMessage(message) &&
151 !ProcessFailedPasswordMessage(message) &&
152 !ProcessStateMessage(message))
153 << "OpenVPN management message ignored: " << message;
154}
155
156bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
157 return StartsWithASCII(message, ">INFO:", true);
158}
159
160bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
161 const string &message) {
162 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
163 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200164 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200165 string tag = ParseNeedPasswordTag(message);
166 if (tag == "Auth") {
Darin Petkov683942b2012-03-27 18:00:04 +0200167 if (message.find("SC:") != string::npos) {
Darin Petkove0d5dd12012-04-04 16:10:48 +0200168 PerformStaticChallenge(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200169 } else {
170 NOTIMPLEMENTED()
Darin Petkov0440b9b2012-04-17 16:11:56 +0200171 << ": User/password (no-OTP) authentication not implemented.";
172 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200173 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200174 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
175 SupplyTPMToken(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200176 }
Darin Petkov271fe522012-03-27 13:47:29 +0200177 return true;
178}
179
Darin Petkove0d5dd12012-04-04 16:10:48 +0200180// static
181string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
182 size_t start = message.find('\'');
183 if (start == string::npos) {
184 return string();
185 }
186 size_t end = message.find('\'', start + 1);
187 if (end == string::npos) {
188 return string();
189 }
190 return message.substr(start + 1, end - start - 1);
191}
192
193void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov683942b2012-03-27 18:00:04 +0200194 string user =
195 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
196 string password =
197 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
198 string otp =
199 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
200 if (user.empty() || password.empty() || otp.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200201 NOTIMPLEMENTED() << ": Missing credentials.";
202 driver_->Cleanup(Service::kStateFailure);
Darin Petkov683942b2012-03-27 18:00:04 +0200203 return;
204 }
205 gchar *b64_password =
206 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
207 password.size());
208 gchar *b64_otp =
209 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
210 otp.size());
211 if (!b64_password || !b64_otp) {
212 LOG(ERROR) << "Unable to base64-encode credentials.";
213 return;
214 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200215 SendUsername(tag, user);
216 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200217 glib_->Free(b64_otp);
218 glib_->Free(b64_password);
219 // Don't reuse OTP.
220 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
221}
222
Darin Petkove0d5dd12012-04-04 16:10:48 +0200223void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
224 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
225 if (pin.empty()) {
Darin Petkov0440b9b2012-04-17 16:11:56 +0200226 NOTIMPLEMENTED() << ": Missing PIN.";
227 driver_->Cleanup(Service::kStateFailure);
Darin Petkove0d5dd12012-04-04 16:10:48 +0200228 return;
229 }
230 SendPassword(tag, pin);
231}
232
Darin Petkov271fe522012-03-27 13:47:29 +0200233bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
234 const string &message) {
235 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
236 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200237 }
Darin Petkov271fe522012-03-27 13:47:29 +0200238 NOTIMPLEMENTED();
Darin Petkov0440b9b2012-04-17 16:11:56 +0200239 driver_->Cleanup(Service::kStateFailure);
Darin Petkov271fe522012-03-27 13:47:29 +0200240 return true;
241}
242
243// >STATE:* message support. State messages are of the form:
244// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
245// where:
246// <date> is the current time (since epoch) in seconds
247// <state> is one of:
248// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
249// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
250// <detail> is a free-form string giving details about the state change
251// <local-ip> is a dotted-quad for the local IPv4 address (when available)
252// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
253bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
254 if (!StartsWithASCII(message, ">STATE:", true)) {
255 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200256 }
Darin Petkov271fe522012-03-27 13:47:29 +0200257 vector<string> details;
258 SplitString(message, ',', &details);
259 if (details.size() > 1) {
260 if (details[1] == "RECONNECTING") {
261 driver_->OnReconnecting();
262 }
263 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200264 }
Darin Petkov271fe522012-03-27 13:47:29 +0200265 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200266}
267
268void OpenVPNManagementServer::Send(const string &data) {
Darin Petkov683942b2012-03-27 18:00:04 +0200269 VLOG(2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200270 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
271 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200272 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200273}
274
275void OpenVPNManagementServer::SendState(const string &state) {
276 VLOG(2) << __func__ << "(" << state << ")";
277 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100278}
279
Darin Petkov683942b2012-03-27 18:00:04 +0200280void OpenVPNManagementServer::SendUsername(const string &tag,
281 const string &username) {
282 VLOG(2) << __func__;
283 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
284}
285
286void OpenVPNManagementServer::SendPassword(const string &tag,
287 const string &password) {
288 VLOG(2) << __func__;
289 Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
290}
291
Darin Petkov1c115202012-03-22 15:35:47 +0100292} // namespace shill