Chromium Code Reviews| Index: remoting/host/gnubby_auth_handler_posix.cc |
| diff --git a/remoting/host/gnubby_auth_handler_posix.cc b/remoting/host/gnubby_auth_handler_posix.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..df391e6f1e464a089e1f4b1b66bcc77a4ba23c39 |
| --- /dev/null |
| +++ b/remoting/host/gnubby_auth_handler_posix.cc |
| @@ -0,0 +1,216 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "remoting/host/gnubby_auth_handler_posix.h" |
| + |
| +#include <unistd.h> |
| +#include <utility> |
| +#include <vector> |
| + |
| +#include "base/bind.h" |
| +#include "base/file_util.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/stl_util.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/string_split.h" |
| +#include "base/values.h" |
| +#include "net/socket/unix_domain_socket_posix.h" |
| +#include "remoting/base/logging.h" |
| +#include "remoting/host/gnubby_util.h" |
| +#include "remoting/proto/control.pb.h" |
| +#include "remoting/protocol/client_stub.h" |
| + |
| +namespace remoting { |
| + |
| +namespace { |
| + |
| +// The name of the socket to listen for gnubby requests on. |
| +base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +// STL predicate to match by a StreamListenSocket pointer. |
| +class CompareSocket { |
| + public: |
| + explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {} |
| + |
| + bool operator()(const std::pair<int, net::StreamListenSocket*> element) |
| + const { |
| + return socket_ == element.second; |
| + } |
| + |
| + private: |
| + net::StreamListenSocket* socket_; |
| +}; |
| + |
| +// Socket authentication function that only allows connections from callers with |
| +// the current uid. |
| +bool MatchUid(uid_t user_id, gid_t) { |
| + bool allowed = user_id == getuid(); |
| + if (!allowed) |
| + HOST_LOG << "Refused socket connection from uid " << user_id; |
| + return allowed; |
| +} |
| + |
| +// Returns true if the request data is complete (has at least as many bytes as |
| +// indicated by the size in the first four bytes plus four for the first bytes). |
| +bool IsRequestComplete(const char* data, int data_len) { |
| + if (data_len < 4) |
| + return false; |
| + int expected_length = ((data[0] & 255) << 24) + ((data[1] & 255) << 16) + |
| + ((data[2] & 255) << 8) + (data[3] & 255) + 4; |
| + return expected_length <= data_len; |
| +} |
| + |
| +} // namespace |
| + |
| +GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix( |
| + protocol::ClientStub* client_stub) |
| + : client_stub_(client_stub), last_connection_id_(0) { |
| + DCHECK(client_stub_); |
| +} |
| + |
| +GnubbyAuthHandlerPosix::~GnubbyAuthHandlerPosix() { |
| + STLDeleteValues(&active_sockets_); |
| +} |
| + |
| +// static |
| +scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( |
| + protocol::ClientStub* client_stub) { |
| + return scoped_ptr<GnubbyAuthHandler>(new GnubbyAuthHandlerPosix(client_stub)); |
| +} |
| + |
| +// static |
| +void GnubbyAuthHandler::SetGnubbySocketName( |
| + const base::FilePath& gnubby_socket_name) { |
| + g_gnubby_socket_name.Get() = gnubby_socket_name; |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::DeliverClientMessage(const std::string& message) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + std::vector<std::string> values; |
| + base::SplitString(message, ' ', &values); |
|
Sergey Ulanov
2014/02/14 07:31:59
This will also split the third component that is i
psj
2014/02/15 00:01:34
Done.
|
| + |
| + if (values[0] == "control") { |
| + if (values[1] == "auth-v1") { |
| + CreateAuthorizationSocket(); |
| + } |
| + } else if (values[0] == "data") { |
| + int connection_id = 0; |
| + base::StringToInt(values[1], &connection_id); |
| + |
| + ActiveSockets::iterator iter = active_sockets_.find(connection_id); |
| + if (iter != active_sockets_.end()) { |
| + HOST_LOG << "Sending gnubby response"; |
| + |
| + std::string reply; |
| + GetGnubbyResponseFromJson(values[2], &reply); |
|
Sergey Ulanov
2014/02/14 07:31:59
You need to verify that |values| contains at least
psj
2014/02/15 00:01:34
Ack
|
| + |
| + iter->second->Send(reply); |
| + } |
| + } |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::DeliverHostDataMessage(int connection_id, |
| + const std::string& data) |
| + const { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + protocol::ExtensionMessage message; |
| + message.set_type("gnubby-auth"); |
| + message.set_data("data " + base::IntToString(connection_id) + " " + data); |
| + |
| + client_stub_->DeliverHostMessage(message); |
| +} |
| + |
| +bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting( |
| + net::StreamListenSocket* socket) const { |
| + return std::find_if(active_sockets_.begin(), |
| + active_sockets_.end(), |
| + CompareSocket(socket)) != active_sockets_.end(); |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::DidAccept( |
| + net::StreamListenSocket* server, |
| + scoped_ptr<net::StreamListenSocket> socket) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + active_sockets_[++last_connection_id_] = socket.release(); |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket, |
| + const char* data, |
| + int len) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + ActiveSockets::iterator socket_iter = std::find_if( |
| + active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); |
| + if (socket_iter != active_sockets_.end()) { |
| + int connection_id = socket_iter->first; |
| + |
| + ActiveRequests::iterator request_iter = |
| + active_requests_.find(connection_id); |
| + if (request_iter != active_requests_.end()) { |
| + std::vector<char>& saved_vector = request_iter->second; |
| + saved_vector.insert(saved_vector.end(), data, data + len); |
| + |
| + if (IsRequestComplete(saved_vector.data(), saved_vector.size())) { |
| + ProcessGnubbyRequest( |
| + connection_id, saved_vector.data(), saved_vector.size()); |
| + active_requests_.erase(request_iter); |
| + } |
| + } else if (IsRequestComplete(data, len)) { |
| + ProcessGnubbyRequest(connection_id, data, len); |
| + } else { |
| + active_requests_[connection_id] = std::vector<char>(data, data + len); |
| + } |
| + } |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + ActiveSockets::iterator iter = std::find_if( |
| + active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); |
| + if (iter != active_sockets_.end()) { |
| + active_requests_.erase(iter->first); |
| + |
| + delete iter->second; |
| + active_sockets_.erase(iter); |
| + } |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() { |
| + DCHECK(CalledOnValidThread()); |
| + |
| + if (!g_gnubby_socket_name.Get().empty()) { |
| + // If the file already exists, a socket in use error is returned. |
| + base::DeleteFile(g_gnubby_socket_name.Get(), false); |
| + |
| + HOST_LOG << "Listening for gnubby requests on " |
| + << g_gnubby_socket_name.Get().value(); |
| + |
| + auth_socket_ = net::UnixDomainSocket::CreateAndListen( |
| + g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid)); |
| + if (!auth_socket_.get()) { |
| + LOG(ERROR) << "Failed to open socket for gnubby requests"; |
| + } |
| + } else { |
| + HOST_LOG << "No gnubby socket name specified"; |
| + } |
| +} |
| + |
| +void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(int connection_id, |
| + const char* data, |
| + int data_len) { |
| + std::string json; |
| + if (GetJsonFromGnubbyRequest(data, data_len, &json)) { |
| + HOST_LOG << "Received gnubby request"; |
| + DeliverHostDataMessage(connection_id, json); |
| + } else { |
| + LOG(ERROR) << "Could not decode gnubby request"; |
| + } |
| +} |
| + |
| +} // namespace remoting |