Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1256)

Side by Side Diff: remoting/host/gnubby_auth_handler_posix.cc

Issue 205493005: Do minimal processing of gnubby data. Add request timeouts and send error (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/gnubby_auth_handler_posix.h" 5 #include "remoting/host/gnubby_auth_handler_posix.h"
6 6
7 #include <unistd.h> 7 #include <unistd.h>
8 #include <utility> 8 #include <utility>
9 #include <vector>
10 9
11 #include "base/bind.h" 10 #include "base/bind.h"
12 #include "base/file_util.h" 11 #include "base/file_util.h"
13 #include "base/json/json_reader.h" 12 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h" 13 #include "base/json/json_writer.h"
15 #include "base/lazy_instance.h" 14 #include "base/lazy_instance.h"
16 #include "base/stl_util.h" 15 #include "base/stl_util.h"
17 #include "base/values.h" 16 #include "base/values.h"
18 #include "net/socket/unix_domain_socket_posix.h" 17 #include "net/socket/unix_domain_socket_posix.h"
19 #include "remoting/base/logging.h" 18 #include "remoting/base/logging.h"
20 #include "remoting/host/gnubby_util.h" 19 #include "remoting/host/gnubby_socket.h"
21 #include "remoting/proto/control.pb.h" 20 #include "remoting/proto/control.pb.h"
22 #include "remoting/protocol/client_stub.h" 21 #include "remoting/protocol/client_stub.h"
23 22
24 namespace remoting { 23 namespace remoting {
25 24
26 namespace { 25 namespace {
27 26
28 const int kMaxRequestLength = 4096;
29
30 const char kConnectionId[] = "connectionId"; 27 const char kConnectionId[] = "connectionId";
31 const char kControlMessage[] = "control"; 28 const char kControlMessage[] = "control";
32 const char kControlOption[] = "option"; 29 const char kControlOption[] = "option";
33 const char kDataMessage[] = "data"; 30 const char kDataMessage[] = "data";
31 const char kDataPayload[] = "data";
32 const char kErrorMessage[] = "error";
34 const char kGnubbyAuthMessage[] = "gnubby-auth"; 33 const char kGnubbyAuthMessage[] = "gnubby-auth";
35 const char kGnubbyAuthV1[] = "auth-v1"; 34 const char kGnubbyAuthV1[] = "auth-v1";
36 const char kJSONMessage[] = "jsonMessage";
37 const char kMessageType[] = "type"; 35 const char kMessageType[] = "type";
38 36
39 // The name of the socket to listen for gnubby requests on. 37 // The name of the socket to listen for gnubby requests on.
40 base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = 38 base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name =
41 LAZY_INSTANCE_INITIALIZER; 39 LAZY_INSTANCE_INITIALIZER;
42 40
43 // STL predicate to match by a StreamListenSocket pointer. 41 // STL predicate to match by a StreamListenSocket pointer.
44 class CompareSocket { 42 class CompareSocket {
45 public: 43 public:
46 explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {} 44 explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {}
47 45
48 bool operator()(const std::pair<int, net::StreamListenSocket*> element) 46 bool operator()(const std::pair<int, GnubbySocket*> element) const {
49 const { 47 return element.second->IsSocket(socket_);
50 return socket_ == element.second;
51 } 48 }
52 49
53 private: 50 private:
54 net::StreamListenSocket* socket_; 51 net::StreamListenSocket* socket_;
55 }; 52 };
56 53
57 // Socket authentication function that only allows connections from callers with 54 // Socket authentication function that only allows connections from callers with
58 // the current uid. 55 // the current uid.
59 bool MatchUid(uid_t user_id, gid_t) { 56 bool MatchUid(uid_t user_id, gid_t) {
60 bool allowed = user_id == getuid(); 57 bool allowed = user_id == getuid();
61 if (!allowed) 58 if (!allowed)
62 HOST_LOG << "Refused socket connection from uid " << user_id; 59 HOST_LOG << "Refused socket connection from uid " << user_id;
63 return allowed; 60 return allowed;
64 } 61 }
65 62
66 // Returns the request data length from the first four data bytes. 63 // Returns the command code (the first byte of the data) if it exists, or -1 if
67 int GetRequestLength(const char* data) { 64 // the data is empty.
68 return ((data[0] & 255) << 24) + ((data[1] & 255) << 16) + 65 unsigned int GetCommandCode(const std::string& data) {
69 ((data[2] & 255) << 8) + (data[3] & 255) + 4; 66 return data.empty() ? -1 : static_cast<unsigned int>(data[0]);
70 } 67 }
71 68
72 // Returns true if the request data is complete (has at least as many bytes as 69 // Creates a string of byte data from a ListValue of numbers. Returns true if
73 // indicated by the size in the first four bytes plus four for the first bytes). 70 // all of the list elements are numbers.
74 bool IsRequestComplete(const char* data, int data_len) { 71 bool ConvertListValueToString(base::ListValue* bytes, std::string* out) {
75 if (data_len < 4) 72 out->clear();
76 return false;
77 return GetRequestLength(data) <= data_len;
78 }
79 73
80 // Returns true if the request data size is bigger than the threshold. 74 unsigned int byte_count = bytes->GetSize();
81 bool IsRequestTooLarge(const char* data, int data_len, int max_len) { 75 if (byte_count != 0) {
82 if (data_len < 4) 76 out->reserve(byte_count);
83 return false; 77 for (unsigned int i = 0; i < byte_count; i++) {
84 return GetRequestLength(data) > max_len; 78 int value;
79 if (bytes->GetInteger(i, &value)) {
80 out->push_back(static_cast<char>(value));
81 } else {
82 return false;
83 }
84 }
85 }
86 return true;
85 } 87 }
86 88
87 } // namespace 89 } // namespace
88 90
89 GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix( 91 GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix(
90 protocol::ClientStub* client_stub) 92 protocol::ClientStub* client_stub)
91 : client_stub_(client_stub), last_connection_id_(0) { 93 : client_stub_(client_stub), last_connection_id_(0) {
92 DCHECK(client_stub_); 94 DCHECK(client_stub_);
93 } 95 }
94 96
(...skipping 27 matching lines...) Expand all
122 124
123 if (type == kControlMessage) { 125 if (type == kControlMessage) {
124 std::string option; 126 std::string option;
125 if (client_message->GetString(kControlOption, &option) && 127 if (client_message->GetString(kControlOption, &option) &&
126 option == kGnubbyAuthV1) { 128 option == kGnubbyAuthV1) {
127 CreateAuthorizationSocket(); 129 CreateAuthorizationSocket();
128 } else { 130 } else {
129 LOG(ERROR) << "Invalid gnubby-auth control option"; 131 LOG(ERROR) << "Invalid gnubby-auth control option";
130 } 132 }
131 } else if (type == kDataMessage) { 133 } else if (type == kDataMessage) {
132 int connection_id; 134 ActiveSockets::iterator iter = GetSocketForMessage(client_message);
133 std::string json_message; 135 if (iter != active_sockets_.end()) {
134 if (client_message->GetInteger(kConnectionId, &connection_id) && 136 base::ListValue* bytes;
135 client_message->GetString(kJSONMessage, &json_message)) { 137 std::string response;
136 ActiveSockets::iterator iter = active_sockets_.find(connection_id); 138 if (client_message->GetList(kDataPayload, &bytes) &&
137 if (iter != active_sockets_.end()) { 139 ConvertListValueToString(bytes, &response)) {
138 HOST_LOG << "Sending gnubby response"; 140 HOST_LOG << "Sending gnubby response: " << GetCommandCode(response);
139 141 iter->second->SendResponse(response);
140 std::string response;
141 GetGnubbyResponseFromJson(json_message, &response);
142 iter->second->Send(response);
143 } else { 142 } else {
144 LOG(ERROR) << "Received gnubby-auth data for unknown connection"; 143 LOG(ERROR) << "Invalid gnubby data";
144 SendErrorAndCloseActiveSocket(iter);
145 } 145 }
146 } else { 146 } else {
147 LOG(ERROR) << "Invalid gnubby-auth data message"; 147 LOG(ERROR) << "Unknown gnubby-auth data connection";
148 }
149 } else if (type == kErrorMessage) {
150 ActiveSockets::iterator iter = GetSocketForMessage(client_message);
151 if (iter != active_sockets_.end()) {
152 HOST_LOG << "Sending gnubby error";
153 SendErrorAndCloseActiveSocket(iter);
154 } else {
155 LOG(ERROR) << "Unknown gnubby-auth error connection";
148 } 156 }
149 } else { 157 } else {
150 LOG(ERROR) << "Unknown gnubby-auth message type: " << type; 158 LOG(ERROR) << "Unknown gnubby-auth message type: " << type;
151 } 159 }
152 } 160 }
153 } 161 }
154 162
155 void GnubbyAuthHandlerPosix::DeliverHostDataMessage(int connection_id, 163 void GnubbyAuthHandlerPosix::DeliverHostDataMessage(
156 const std::string& data) 164 int connection_id,
157 const { 165 const std::string& data) const {
158 DCHECK(CalledOnValidThread()); 166 DCHECK(CalledOnValidThread());
159 167
160 base::DictionaryValue request; 168 base::DictionaryValue request;
161 request.SetString(kMessageType, kDataMessage); 169 request.SetString(kMessageType, kDataMessage);
162 request.SetInteger(kConnectionId, connection_id); 170 request.SetInteger(kConnectionId, connection_id);
163 request.SetString(kJSONMessage, data); 171
172 base::ListValue* bytes = new base::ListValue();
173 if (!bytes) {
174 LOG(ERROR) << "Failed to allocate list";
175 return;
176 }
177 for (std::string::const_iterator i = data.begin(); i != data.end(); ++i) {
178 bytes->AppendInteger(static_cast<unsigned char>(*i));
179 }
180 request.Set(kDataPayload, bytes);
164 181
165 std::string request_json; 182 std::string request_json;
166 if (!base::JSONWriter::Write(&request, &request_json)) { 183 if (!base::JSONWriter::Write(&request, &request_json)) {
167 LOG(ERROR) << "Failed to create request json"; 184 LOG(ERROR) << "Failed to create request json";
168 return; 185 return;
169 } 186 }
170 187
171 protocol::ExtensionMessage message; 188 protocol::ExtensionMessage message;
172 message.set_type(kGnubbyAuthMessage); 189 message.set_type(kGnubbyAuthMessage);
173 message.set_data(request_json); 190 message.set_data(request_json);
174 191
175 client_stub_->DeliverHostMessage(message); 192 client_stub_->DeliverHostMessage(message);
176 } 193 }
177 194
178 bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting( 195 bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting(
179 net::StreamListenSocket* socket) const { 196 net::StreamListenSocket* socket) const {
180 return std::find_if(active_sockets_.begin(), 197 return std::find_if(active_sockets_.begin(),
181 active_sockets_.end(), 198 active_sockets_.end(),
182 CompareSocket(socket)) != active_sockets_.end(); 199 CompareSocket(socket)) != active_sockets_.end();
183 } 200 }
184 201
202 int GnubbyAuthHandlerPosix::GetConnectionIdForTesting(
203 net::StreamListenSocket* socket) const {
204 ActiveSockets::const_iterator iter = std::find_if(
205 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
206 return iter->first;
207 }
208
209 GnubbySocket* GnubbyAuthHandlerPosix::GetGnubbySocketForTesting(
210 net::StreamListenSocket* socket) const {
211 ActiveSockets::const_iterator iter = std::find_if(
212 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
213 return iter->second;
214 }
215
185 void GnubbyAuthHandlerPosix::DidAccept( 216 void GnubbyAuthHandlerPosix::DidAccept(
186 net::StreamListenSocket* server, 217 net::StreamListenSocket* server,
187 scoped_ptr<net::StreamListenSocket> socket) { 218 scoped_ptr<net::StreamListenSocket> socket) {
188 DCHECK(CalledOnValidThread()); 219 DCHECK(CalledOnValidThread());
189 220
190 active_sockets_[++last_connection_id_] = socket.release(); 221 int connection_id = ++last_connection_id_;
222 active_sockets_[connection_id] =
223 new GnubbySocket(socket.Pass(),
224 base::Bind(&GnubbyAuthHandlerPosix::RequestTimedOut,
225 base::Unretained(this),
226 connection_id));
191 } 227 }
192 228
193 void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket, 229 void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket,
194 const char* data, 230 const char* data,
195 int len) { 231 int len) {
196 DCHECK(CalledOnValidThread()); 232 DCHECK(CalledOnValidThread());
197 233
198 ActiveSockets::iterator socket_iter = std::find_if( 234 ActiveSockets::iterator iter = std::find_if(
199 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); 235 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
200 if (socket_iter != active_sockets_.end()) { 236 if (iter != active_sockets_.end()) {
201 int connection_id = socket_iter->first; 237 GnubbySocket* gnubby_socket = iter->second;
202 238 gnubby_socket->AddRequestData(data, len);
203 ActiveRequests::iterator request_iter = 239 if (gnubby_socket->IsRequestTooLarge()) {
204 active_requests_.find(connection_id); 240 SendErrorAndCloseActiveSocket(iter);
205 if (request_iter != active_requests_.end()) { 241 } else if (gnubby_socket->IsRequestComplete()) {
206 std::vector<char>& saved_vector = request_iter->second; 242 std::string request_data;
207 if (IsRequestTooLarge( 243 gnubby_socket->GetAndClearRequestData(&request_data);
208 saved_vector.data(), saved_vector.size(), kMaxRequestLength)) { 244 ProcessGnubbyRequest(iter->first, request_data);
209 // We can't close a StreamListenSocket; throw away everything but the
210 // size bytes.
211 saved_vector.resize(4);
212 return;
213 }
214 saved_vector.insert(saved_vector.end(), data, data + len);
215
216 if (IsRequestComplete(saved_vector.data(), saved_vector.size())) {
217 ProcessGnubbyRequest(
218 connection_id, saved_vector.data(), saved_vector.size());
219 active_requests_.erase(request_iter);
220 }
221 } else if (IsRequestComplete(data, len)) {
222 ProcessGnubbyRequest(connection_id, data, len);
223 } else {
224 if (IsRequestTooLarge(data, len, kMaxRequestLength)) {
225 // Only save the size bytes.
226 active_requests_[connection_id] = std::vector<char>(data, data + 4);
227 } else {
228 active_requests_[connection_id] = std::vector<char>(data, data + len);
229 }
230 } 245 }
246 } else {
247 LOG(ERROR) << "Received data for unknown connection";
231 } 248 }
232 } 249 }
233 250
234 void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) { 251 void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) {
235 DCHECK(CalledOnValidThread()); 252 DCHECK(CalledOnValidThread());
236 253
237 ActiveSockets::iterator iter = std::find_if( 254 ActiveSockets::iterator iter = std::find_if(
238 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); 255 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket));
239 if (iter != active_sockets_.end()) { 256 if (iter != active_sockets_.end()) {
240 active_requests_.erase(iter->first);
241
242 delete iter->second; 257 delete iter->second;
243 active_sockets_.erase(iter); 258 active_sockets_.erase(iter);
244 } 259 }
245 } 260 }
246 261
247 void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() { 262 void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() {
248 DCHECK(CalledOnValidThread()); 263 DCHECK(CalledOnValidThread());
249 264
250 if (!g_gnubby_socket_name.Get().empty()) { 265 if (!g_gnubby_socket_name.Get().empty()) {
251 // If the file already exists, a socket in use error is returned. 266 // If the file already exists, a socket in use error is returned.
252 base::DeleteFile(g_gnubby_socket_name.Get(), false); 267 base::DeleteFile(g_gnubby_socket_name.Get(), false);
253 268
254 HOST_LOG << "Listening for gnubby requests on " 269 HOST_LOG << "Listening for gnubby requests on "
255 << g_gnubby_socket_name.Get().value(); 270 << g_gnubby_socket_name.Get().value();
256 271
257 auth_socket_ = net::UnixDomainSocket::CreateAndListen( 272 auth_socket_ = net::UnixDomainSocket::CreateAndListen(
258 g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid)); 273 g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid));
259 if (!auth_socket_.get()) { 274 if (!auth_socket_.get()) {
260 LOG(ERROR) << "Failed to open socket for gnubby requests"; 275 LOG(ERROR) << "Failed to open socket for gnubby requests";
261 } 276 }
262 } else { 277 } else {
263 HOST_LOG << "No gnubby socket name specified"; 278 HOST_LOG << "No gnubby socket name specified";
264 } 279 }
265 } 280 }
266 281
267 void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(int connection_id, 282 void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(
268 const char* data, 283 int connection_id,
269 int data_len) { 284 const std::string& request_data) {
270 std::string json; 285 HOST_LOG << "Received gnubby request: " << GetCommandCode(request_data);
271 if (GetJsonFromGnubbyRequest(data, data_len, &json)) { 286 DeliverHostDataMessage(connection_id, request_data);
272 HOST_LOG << "Received gnubby request"; 287 }
273 DeliverHostDataMessage(connection_id, json); 288
274 } else { 289 GnubbyAuthHandlerPosix::ActiveSockets::iterator
275 LOG(ERROR) << "Could not decode gnubby request"; 290 GnubbyAuthHandlerPosix::GetSocketForMessage(base::DictionaryValue* message) {
291 int connection_id;
292 if (message->GetInteger(kConnectionId, &connection_id)) {
293 return active_sockets_.find(connection_id);
294 }
295 return active_sockets_.end();
296 }
297
298 void GnubbyAuthHandlerPosix::SendErrorAndCloseActiveSocket(
299 const ActiveSockets::iterator& iter) {
300 if (iter != active_sockets_.end()) {
301 iter->second->SendSshError();
302
303 delete iter->second;
304 active_sockets_.erase(iter);
276 } 305 }
277 } 306 }
278 307
308 void GnubbyAuthHandlerPosix::RequestTimedOut(int connection_id) {
309 HOST_LOG << "Gnubby request timed out";
310 SendErrorAndCloseActiveSocket(active_sockets_.find(connection_id));
311 }
312
279 } // namespace remoting 313 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698