blob: cf06d1071ce0a57c6d588f8f307b5fa1eea13dbc [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 Petkov78f63262012-03-26 01:30:24 +02007#include <netinet/in.h>
8
9#include <base/bind.h>
Darin Petkov1c115202012-03-22 15:35:47 +010010#include <base/logging.h>
Darin Petkov78f63262012-03-26 01:30:24 +020011#include <base/string_split.h>
12#include <base/string_util.h>
13#include <base/stringprintf.h>
Darin Petkov683942b2012-03-27 18:00:04 +020014#include <chromeos/dbus/service_constants.h>
Darin Petkov78f63262012-03-26 01:30:24 +020015
16#include "shill/event_dispatcher.h"
Darin Petkov683942b2012-03-27 18:00:04 +020017#include "shill/glib.h"
Darin Petkov271fe522012-03-27 13:47:29 +020018#include "shill/openvpn_driver.h"
Darin Petkov78f63262012-03-26 01:30:24 +020019#include "shill/sockets.h"
20
21using base::Bind;
22using base::SplitString;
23using base::StringPrintf;
24using std::string;
25using std::vector;
Darin Petkov1c115202012-03-22 15:35:47 +010026
27namespace shill {
28
Darin Petkov683942b2012-03-27 18:00:04 +020029OpenVPNManagementServer::OpenVPNManagementServer(OpenVPNDriver *driver,
30 GLib *glib)
Darin Petkov78f63262012-03-26 01:30:24 +020031 : driver_(driver),
Darin Petkov683942b2012-03-27 18:00:04 +020032 glib_(glib),
Darin Petkov78f63262012-03-26 01:30:24 +020033 weak_ptr_factory_(this),
34 ready_callback_(Bind(&OpenVPNManagementServer::OnReady,
35 weak_ptr_factory_.GetWeakPtr())),
36 input_callback_(Bind(&OpenVPNManagementServer::OnInput,
37 weak_ptr_factory_.GetWeakPtr())),
38 sockets_(NULL),
39 socket_(-1),
40 dispatcher_(NULL),
41 connected_socket_(-1) {}
Darin Petkov1c115202012-03-22 15:35:47 +010042
Darin Petkov78f63262012-03-26 01:30:24 +020043OpenVPNManagementServer::~OpenVPNManagementServer() {
44 Stop();
45}
Darin Petkov1c115202012-03-22 15:35:47 +010046
Darin Petkov78f63262012-03-26 01:30:24 +020047bool OpenVPNManagementServer::Start(EventDispatcher *dispatcher,
48 Sockets *sockets) {
Darin Petkov1c115202012-03-22 15:35:47 +010049 VLOG(2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +020050 if (sockets_) {
51 return true;
52 }
53
Darin Petkov271fe522012-03-27 13:47:29 +020054 int socket = sockets->Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Darin Petkov78f63262012-03-26 01:30:24 +020055 if (socket < 0) {
56 PLOG(ERROR) << "Unable to create management server socket.";
57 return false;
58 }
59
60 struct sockaddr_in addr;
61 socklen_t addrlen = sizeof(addr);
62 memset(&addr, 0, sizeof(addr));
63 addr.sin_family = AF_INET;
64 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
65 if (sockets->Bind(
66 socket, reinterpret_cast<struct sockaddr *>(&addr), addrlen) < 0 ||
67 sockets->Listen(socket, 1) < 0 ||
68 sockets->GetSockName(
69 socket, reinterpret_cast<struct sockaddr *>(&addr), &addrlen) < 0) {
70 PLOG(ERROR) << "Socket setup failed.";
71 sockets->Close(socket);
72 return false;
73 }
74
75 VLOG(2) << "Listening socket: " << socket;
Darin Petkov271fe522012-03-27 13:47:29 +020076 sockets_ = sockets;
Darin Petkov78f63262012-03-26 01:30:24 +020077 socket_ = socket;
78 ready_handler_.reset(
79 dispatcher->CreateReadyHandler(
80 socket, IOHandler::kModeInput, ready_callback_));
81 dispatcher_ = dispatcher;
82 return true;
83}
84
85void OpenVPNManagementServer::Stop() {
86 VLOG(2) << __func__;
87 if (!sockets_) {
88 return;
89 }
90 input_handler_.reset();
91 if (connected_socket_ >= 0) {
92 sockets_->Close(connected_socket_);
93 connected_socket_ = -1;
94 }
95 dispatcher_ = NULL;
96 ready_handler_.reset();
97 if (socket_ >= 0) {
98 sockets_->Close(socket_);
99 socket_ = -1;
100 }
101 sockets_ = NULL;
102}
103
104void OpenVPNManagementServer::OnReady(int fd) {
105 VLOG(2) << __func__ << "(" << fd << ")";
106 connected_socket_ = sockets_->Accept(fd, NULL, NULL);
107 if (connected_socket_ < 0) {
108 PLOG(ERROR) << "Connected socket accept failed.";
109 return;
110 }
111 ready_handler_.reset();
112 input_handler_.reset(
113 dispatcher_->CreateInputHandler(connected_socket_, input_callback_));
114 SendState("on");
115}
116
117void OpenVPNManagementServer::OnInput(InputData *data) {
118 VLOG(2) << __func__ << "(" << data->len << ")";
119 vector<string> messages;
120 SplitString(
121 string(reinterpret_cast<char *>(data->buf), data->len), '\n', &messages);
122 for (vector<string>::const_iterator it = messages.begin();
123 it != messages.end(); ++it) {
124 ProcessMessage(*it);
125 }
126}
127
128void OpenVPNManagementServer::ProcessMessage(const string &message) {
129 VLOG(2) << __func__ << "(" << message << ")";
Darin Petkov271fe522012-03-27 13:47:29 +0200130 LOG_IF(WARNING,
131 !ProcessInfoMessage(message) &&
132 !ProcessNeedPasswordMessage(message) &&
133 !ProcessFailedPasswordMessage(message) &&
134 !ProcessStateMessage(message))
135 << "OpenVPN management message ignored: " << message;
136}
137
138bool OpenVPNManagementServer::ProcessInfoMessage(const string &message) {
139 return StartsWithASCII(message, ">INFO:", true);
140}
141
142bool OpenVPNManagementServer::ProcessNeedPasswordMessage(
143 const string &message) {
144 if (!StartsWithASCII(message, ">PASSWORD:Need ", true)) {
145 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200146 }
Darin Petkov683942b2012-03-27 18:00:04 +0200147 if (message.find("'Auth'") != string::npos) {
148 if (message.find("SC:") != string::npos) {
149 PerformStaticChallenge();
150 } else {
151 NOTIMPLEMENTED()
152 << "User/password (no-OTP) authentication not implemented.";
153 }
154 }
Darin Petkov271fe522012-03-27 13:47:29 +0200155 return true;
156}
157
Darin Petkov683942b2012-03-27 18:00:04 +0200158void OpenVPNManagementServer::PerformStaticChallenge() {
159 string user =
160 driver_->args()->LookupString(flimflam::kOpenVPNUserProperty, "");
161 string password =
162 driver_->args()->LookupString(flimflam::kOpenVPNPasswordProperty, "");
163 string otp =
164 driver_->args()->LookupString(flimflam::kOpenVPNOTPProperty, "");
165 if (user.empty() || password.empty() || otp.empty()) {
166 NOTIMPLEMENTED() << "Missing credentials.";
167 return;
168 }
169 gchar *b64_password =
170 glib_->Base64Encode(reinterpret_cast<const guchar *>(password.data()),
171 password.size());
172 gchar *b64_otp =
173 glib_->Base64Encode(reinterpret_cast<const guchar *>(otp.data()),
174 otp.size());
175 if (!b64_password || !b64_otp) {
176 LOG(ERROR) << "Unable to base64-encode credentials.";
177 return;
178 }
179 SendUsername("Auth", user);
180 SendPassword("Auth", StringPrintf("SCRV1:%s:%s", b64_password, b64_otp));
181 glib_->Free(b64_otp);
182 glib_->Free(b64_password);
183 // Don't reuse OTP.
184 driver_->args()->RemoveString(flimflam::kOpenVPNOTPProperty);
185}
186
Darin Petkov271fe522012-03-27 13:47:29 +0200187bool OpenVPNManagementServer::ProcessFailedPasswordMessage(
188 const string &message) {
189 if (!StartsWithASCII(message, ">PASSWORD:Verification Failed:", true)) {
190 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200191 }
Darin Petkov271fe522012-03-27 13:47:29 +0200192 NOTIMPLEMENTED();
193 return true;
194}
195
196// >STATE:* message support. State messages are of the form:
197// >STATE:<date>,<state>,<detail>,<local-ip>,<remote-ip>
198// where:
199// <date> is the current time (since epoch) in seconds
200// <state> is one of:
201// INITIAL, CONNECTING, WAIT, AUTH, GET_CONFIG, ASSIGN_IP, ADD_ROUTES,
202// CONNECTED, RECONNECTING, EXITING, RESOLVE, TCP_CONNECT
203// <detail> is a free-form string giving details about the state change
204// <local-ip> is a dotted-quad for the local IPv4 address (when available)
205// <remote-ip> is a dotted-quad for the remote IPv4 address (when available)
206bool OpenVPNManagementServer::ProcessStateMessage(const string &message) {
207 if (!StartsWithASCII(message, ">STATE:", true)) {
208 return false;
Darin Petkov78f63262012-03-26 01:30:24 +0200209 }
Darin Petkov271fe522012-03-27 13:47:29 +0200210 vector<string> details;
211 SplitString(message, ',', &details);
212 if (details.size() > 1) {
213 if (details[1] == "RECONNECTING") {
214 driver_->OnReconnecting();
215 }
216 // The rest of the states are currently ignored.
Darin Petkov78f63262012-03-26 01:30:24 +0200217 }
Darin Petkov271fe522012-03-27 13:47:29 +0200218 return true;
Darin Petkov78f63262012-03-26 01:30:24 +0200219}
220
221void OpenVPNManagementServer::Send(const string &data) {
Darin Petkov683942b2012-03-27 18:00:04 +0200222 VLOG(2) << __func__;
Darin Petkov78f63262012-03-26 01:30:24 +0200223 ssize_t len = sockets_->Send(connected_socket_, data.data(), data.size(), 0);
224 PLOG_IF(ERROR, len < 0 || static_cast<size_t>(len) != data.size())
Darin Petkov683942b2012-03-27 18:00:04 +0200225 << "Send failed.";
Darin Petkov78f63262012-03-26 01:30:24 +0200226}
227
228void OpenVPNManagementServer::SendState(const string &state) {
229 VLOG(2) << __func__ << "(" << state << ")";
230 Send(StringPrintf("state %s\n", state.c_str()));
Darin Petkov1c115202012-03-22 15:35:47 +0100231}
232
Darin Petkov683942b2012-03-27 18:00:04 +0200233void OpenVPNManagementServer::SendUsername(const string &tag,
234 const string &username) {
235 VLOG(2) << __func__;
236 Send(StringPrintf("username \"%s\" %s\n", tag.c_str(), username.c_str()));
237}
238
239void OpenVPNManagementServer::SendPassword(const string &tag,
240 const string &password) {
241 VLOG(2) << __func__;
242 Send(StringPrintf("password \"%s\" \"%s\"\n", tag.c_str(), password.c_str()));
243}
244
Darin Petkov1c115202012-03-22 15:35:47 +0100245} // namespace shill