OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "remoting/host/gnubby_auth_handler_posix.h" |
| 6 |
| 7 #include <unistd.h> |
| 8 #include <utility> |
| 9 #include <vector> |
| 10 |
| 11 #include "base/bind.h" |
| 12 #include "base/file_util.h" |
| 13 #include "base/json/json_reader.h" |
| 14 #include "base/json/json_writer.h" |
| 15 #include "base/lazy_instance.h" |
| 16 #include "base/stl_util.h" |
| 17 #include "base/values.h" |
| 18 #include "net/socket/unix_domain_socket_posix.h" |
| 19 #include "remoting/base/logging.h" |
| 20 #include "remoting/host/gnubby_util.h" |
| 21 #include "remoting/proto/control.pb.h" |
| 22 #include "remoting/protocol/client_stub.h" |
| 23 |
| 24 namespace remoting { |
| 25 |
| 26 namespace { |
| 27 |
| 28 const int kMaxRequestLength = 4096; |
| 29 |
| 30 const char kConnectionId[] = "connectionId"; |
| 31 const char kControlMessage[] = "control"; |
| 32 const char kControlOption[] = "option"; |
| 33 const char kDataMessage[] = "data"; |
| 34 const char kGnubbyAuthMessage[] = "gnubby-auth"; |
| 35 const char kGnubbyAuthV1[] = "auth-v1"; |
| 36 const char kJSONMessage[] = "jsonMessage"; |
| 37 const char kMessageType[] = "type"; |
| 38 |
| 39 // The name of the socket to listen for gnubby requests on. |
| 40 base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = |
| 41 LAZY_INSTANCE_INITIALIZER; |
| 42 |
| 43 // STL predicate to match by a StreamListenSocket pointer. |
| 44 class CompareSocket { |
| 45 public: |
| 46 explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {} |
| 47 |
| 48 bool operator()(const std::pair<int, net::StreamListenSocket*> element) |
| 49 const { |
| 50 return socket_ == element.second; |
| 51 } |
| 52 |
| 53 private: |
| 54 net::StreamListenSocket* socket_; |
| 55 }; |
| 56 |
| 57 // Socket authentication function that only allows connections from callers with |
| 58 // the current uid. |
| 59 bool MatchUid(uid_t user_id, gid_t) { |
| 60 bool allowed = user_id == getuid(); |
| 61 if (!allowed) |
| 62 HOST_LOG << "Refused socket connection from uid " << user_id; |
| 63 return allowed; |
| 64 } |
| 65 |
| 66 // Returns the request data length from the first four data bytes. |
| 67 int GetRequestLength(const char* data) { |
| 68 return ((data[0] & 255) << 24) + ((data[1] & 255) << 16) + |
| 69 ((data[2] & 255) << 8) + (data[3] & 255) + 4; |
| 70 } |
| 71 |
| 72 // Returns true if the request data is complete (has at least as many bytes as |
| 73 // indicated by the size in the first four bytes plus four for the first bytes). |
| 74 bool IsRequestComplete(const char* data, int data_len) { |
| 75 if (data_len < 4) |
| 76 return false; |
| 77 return GetRequestLength(data) <= data_len; |
| 78 } |
| 79 |
| 80 // Returns true if the request data size is bigger than the threshold. |
| 81 bool IsRequestTooLarge(const char* data, int data_len, int max_len) { |
| 82 if (data_len < 4) |
| 83 return false; |
| 84 return GetRequestLength(data) > max_len; |
| 85 } |
| 86 |
| 87 } // namespace |
| 88 |
| 89 GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix( |
| 90 protocol::ClientStub* client_stub) |
| 91 : client_stub_(client_stub), last_connection_id_(0) { |
| 92 DCHECK(client_stub_); |
| 93 } |
| 94 |
| 95 GnubbyAuthHandlerPosix::~GnubbyAuthHandlerPosix() { |
| 96 STLDeleteValues(&active_sockets_); |
| 97 } |
| 98 |
| 99 // static |
| 100 scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( |
| 101 protocol::ClientStub* client_stub) { |
| 102 return scoped_ptr<GnubbyAuthHandler>(new GnubbyAuthHandlerPosix(client_stub)); |
| 103 } |
| 104 |
| 105 // static |
| 106 void GnubbyAuthHandler::SetGnubbySocketName( |
| 107 const base::FilePath& gnubby_socket_name) { |
| 108 g_gnubby_socket_name.Get() = gnubby_socket_name; |
| 109 } |
| 110 |
| 111 void GnubbyAuthHandlerPosix::DeliverClientMessage(const std::string& message) { |
| 112 DCHECK(CalledOnValidThread()); |
| 113 |
| 114 scoped_ptr<base::Value> value(base::JSONReader::Read(message)); |
| 115 base::DictionaryValue* client_message; |
| 116 if (value && value->GetAsDictionary(&client_message)) { |
| 117 std::string type; |
| 118 if (!client_message->GetString(kMessageType, &type)) { |
| 119 LOG(ERROR) << "Invalid gnubby-auth message"; |
| 120 return; |
| 121 } |
| 122 |
| 123 if (type == kControlMessage) { |
| 124 std::string option; |
| 125 if (client_message->GetString(kControlOption, &option) && |
| 126 option == kGnubbyAuthV1) { |
| 127 CreateAuthorizationSocket(); |
| 128 } else { |
| 129 LOG(ERROR) << "Invalid gnubby-auth control option"; |
| 130 } |
| 131 } else if (type == kDataMessage) { |
| 132 int connection_id; |
| 133 std::string json_message; |
| 134 if (client_message->GetInteger(kConnectionId, &connection_id) && |
| 135 client_message->GetString(kJSONMessage, &json_message)) { |
| 136 ActiveSockets::iterator iter = active_sockets_.find(connection_id); |
| 137 if (iter != active_sockets_.end()) { |
| 138 HOST_LOG << "Sending gnubby response"; |
| 139 |
| 140 std::string response; |
| 141 GetGnubbyResponseFromJson(json_message, &response); |
| 142 iter->second->Send(response); |
| 143 } else { |
| 144 LOG(ERROR) << "Received gnubby-auth data for unknown connection"; |
| 145 } |
| 146 } else { |
| 147 LOG(ERROR) << "Invalid gnubby-auth data message"; |
| 148 } |
| 149 } else { |
| 150 LOG(ERROR) << "Unknown gnubby-auth message type: " << type; |
| 151 } |
| 152 } |
| 153 } |
| 154 |
| 155 void GnubbyAuthHandlerPosix::DeliverHostDataMessage(int connection_id, |
| 156 const std::string& data) |
| 157 const { |
| 158 DCHECK(CalledOnValidThread()); |
| 159 |
| 160 base::DictionaryValue request; |
| 161 request.SetString(kMessageType, kDataMessage); |
| 162 request.SetInteger(kConnectionId, connection_id); |
| 163 request.SetString(kJSONMessage, data); |
| 164 |
| 165 std::string request_json; |
| 166 if (!base::JSONWriter::Write(&request, &request_json)) { |
| 167 LOG(ERROR) << "Failed to create request json"; |
| 168 return; |
| 169 } |
| 170 |
| 171 protocol::ExtensionMessage message; |
| 172 message.set_type(kGnubbyAuthMessage); |
| 173 message.set_data(request_json); |
| 174 |
| 175 client_stub_->DeliverHostMessage(message); |
| 176 } |
| 177 |
| 178 bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting( |
| 179 net::StreamListenSocket* socket) const { |
| 180 return std::find_if(active_sockets_.begin(), |
| 181 active_sockets_.end(), |
| 182 CompareSocket(socket)) != active_sockets_.end(); |
| 183 } |
| 184 |
| 185 void GnubbyAuthHandlerPosix::DidAccept( |
| 186 net::StreamListenSocket* server, |
| 187 scoped_ptr<net::StreamListenSocket> socket) { |
| 188 DCHECK(CalledOnValidThread()); |
| 189 |
| 190 active_sockets_[++last_connection_id_] = socket.release(); |
| 191 } |
| 192 |
| 193 void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket, |
| 194 const char* data, |
| 195 int len) { |
| 196 DCHECK(CalledOnValidThread()); |
| 197 |
| 198 ActiveSockets::iterator socket_iter = std::find_if( |
| 199 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); |
| 200 if (socket_iter != active_sockets_.end()) { |
| 201 int connection_id = socket_iter->first; |
| 202 |
| 203 ActiveRequests::iterator request_iter = |
| 204 active_requests_.find(connection_id); |
| 205 if (request_iter != active_requests_.end()) { |
| 206 std::vector<char>& saved_vector = request_iter->second; |
| 207 if (IsRequestTooLarge( |
| 208 saved_vector.data(), saved_vector.size(), kMaxRequestLength)) { |
| 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 } |
| 231 } |
| 232 } |
| 233 |
| 234 void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) { |
| 235 DCHECK(CalledOnValidThread()); |
| 236 |
| 237 ActiveSockets::iterator iter = std::find_if( |
| 238 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); |
| 239 if (iter != active_sockets_.end()) { |
| 240 active_requests_.erase(iter->first); |
| 241 |
| 242 delete iter->second; |
| 243 active_sockets_.erase(iter); |
| 244 } |
| 245 } |
| 246 |
| 247 void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() { |
| 248 DCHECK(CalledOnValidThread()); |
| 249 |
| 250 if (!g_gnubby_socket_name.Get().empty()) { |
| 251 // If the file already exists, a socket in use error is returned. |
| 252 base::DeleteFile(g_gnubby_socket_name.Get(), false); |
| 253 |
| 254 HOST_LOG << "Listening for gnubby requests on " |
| 255 << g_gnubby_socket_name.Get().value(); |
| 256 |
| 257 auth_socket_ = net::UnixDomainSocket::CreateAndListen( |
| 258 g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid)); |
| 259 if (!auth_socket_.get()) { |
| 260 LOG(ERROR) << "Failed to open socket for gnubby requests"; |
| 261 } |
| 262 } else { |
| 263 HOST_LOG << "No gnubby socket name specified"; |
| 264 } |
| 265 } |
| 266 |
| 267 void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(int connection_id, |
| 268 const char* data, |
| 269 int data_len) { |
| 270 std::string json; |
| 271 if (GetJsonFromGnubbyRequest(data, data_len, &json)) { |
| 272 HOST_LOG << "Received gnubby request"; |
| 273 DeliverHostDataMessage(connection_id, json); |
| 274 } else { |
| 275 LOG(ERROR) << "Could not decode gnubby request"; |
| 276 } |
| 277 } |
| 278 |
| 279 } // namespace remoting |
OLD | NEW |