blob: fa4aa483f611e8b5e60d95c395b3b4ec6d69f5c1 [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()
171 << "User/password (no-OTP) authentication not implemented.";
172 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200173 } else if (StartsWithASCII(tag, "User-Specific TPM Token", true)) {
174 SupplyTPMToken(tag);
Darin Petkov683942b2012-03-27 18:00:04 +0200175 }
Darin Petkov271fe522012-03-27 13:47:29 +0200176 return true;
177}
178
Darin Petkove0d5dd12012-04-04 16:10:48 +0200179// static
180string OpenVPNManagementServer::ParseNeedPasswordTag(const string &message) {
181 size_t start = message.find('\'');
182 if (start == string::npos) {
183 return string();
184 }
185 size_t end = message.find('\'', start + 1);
186 if (end == string::npos) {
187 return string();
188 }
189 return message.substr(start + 1, end - start - 1);
190}
191
192void OpenVPNManagementServer::PerformStaticChallenge(const string &tag) {
Darin Petkov683942b2012-03-27 18:00:04 +0200193 string user =
194 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
195 string password =
196 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
197 string otp =
198 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
199 if (user.empty() || password.empty() || otp.empty()) {
200 NOTIMPLEMENTED() << "Missing credentials.";
201 return;
202 }
203 gchar *b64_password =
204 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
205 password.size());
206 gchar *b64_otp =
207 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
208 otp.size());
209 if (!b64_password || !b64_otp) {
210 LOG(ERROR) << "Unable to base64-encode credentials.";
211 return;
212 }
Darin Petkove0d5dd12012-04-04 16:10:48 +0200213 SendUsername(tag, user);
214 SendPassword(tag, StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
Darin Petkov683942b2012-03-27 18:00:04 +0200215 glib_->Free(b64_otp);
216 glib_->Free(b64_password);
217 // Don't reuse OTP.
218 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
219}
220
Darin Petkove0d5dd12012-04-04 16:10:48 +0200221void OpenVPNManagementServer::SupplyTPMToken(const string &tag) {
222 string pin = driver_->args()->LookupString(flimflam::kOpenVPNPinProperty, "");
223 if (pin.empty()) {
224 NOTIMPLEMENTED() << "Missing PIN.";
225 return;
226 }
227 SendPassword(tag, pin);
228}
229
Darin Petkov271fe522012-03-27 13:47:29 +0200230bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
231 const string &message) {
232 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
233 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200234 }
Darin Petkov271fe522012-03-27 13:47:29 +0200235 NOTIMPLEMENTED();
236 return true;
237}
238
239// >STATE:* message support. State messages are of the form:
240// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
241// where:
242// <date> is the current time (since epoch) in seconds
243// <state> is one of:
244// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
245// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
246// <detail> is a free-form string giving details about the state change
247// <local-ip> is a dotted-quad for the local IPv4 address (when available)
248// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
249bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
250 if (!StartsWithASCII(message, ">STATE:", true)) {
251 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200252 }
Darin Petkov271fe522012-03-27 13:47:29 +0200253 vector<string> details;
254 SplitString(message, ',', &details);
255 if (details.size() > 1) {
256 if (details[1] == "RECONNECTING") {
257 driver_->OnReconnecting();
258 }
259 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200260 }
Darin Petkov271fe522012-03-27 13:47:29 +0200261 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200262}
263
264void OpenVPNManagementServer::Send(const string &data) {
Darin Petkov683942b2012-03-27 18:00:04 +0200265 VLOG(2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200266 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
267 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200268 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200269}
270
271void OpenVPNManagementServer::SendState(const string &state) {
272 VLOG(2) << __func__ << "(" << state << ")";
273 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100274}
275
Darin Petkov683942b2012-03-27 18:00:04 +0200276void OpenVPNManagementServer::SendUsername(const string &tag,
277 const string &username) {
278 VLOG(2) << __func__;
279 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
280}
281
282void OpenVPNManagementServer::SendPassword(const string &tag,
283 const string &password) {
284 VLOG(2) << __func__;
285 Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
286}
287
Darin Petkov1c115202012-03-22 15:35:47 +0100288} // namespace shill