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/lazy_instance.h" | |
14 #include "base/stl_util.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #include "base/strings/string_split.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 // The name of the socket to listen for gnubby requests on. | |
29 base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = | |
30 LAZY_INSTANCE_INITIALIZER; | |
31 | |
32 // STL predicate to match by a StreamListenSocket pointer. | |
33 class CompareSocket { | |
34 public: | |
35 explicit CompareSocket(net::StreamListenSocket* socket) : socket_(socket) {} | |
36 | |
37 bool operator()(const std::pair<int, net::StreamListenSocket*> element) | |
38 const { | |
39 return socket_ == element.second; | |
40 } | |
41 | |
42 private: | |
43 net::StreamListenSocket* socket_; | |
44 }; | |
45 | |
46 // Socket authentication function that only allows connections from callers with | |
47 // the current uid. | |
48 bool MatchUid(uid_t user_id, gid_t) { | |
49 bool allowed = user_id == getuid(); | |
50 if (!allowed) | |
51 HOST_LOG << "Refused socket connection from uid " << user_id; | |
52 return allowed; | |
53 } | |
54 | |
55 // Returns true if the request data is complete (has at least as many bytes as | |
56 // indicated by the size in the first four bytes plus four for the first bytes). | |
57 bool IsRequestComplete(const char* data, int data_len) { | |
58 if (data_len < 4) | |
59 return false; | |
60 int expected_length = ((data[0] & 255) << 24) + ((data[1] & 255) << 16) + | |
61 ((data[2] & 255) << 8) + (data[3] & 255) + 4; | |
62 return expected_length <= data_len; | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 GnubbyAuthHandlerPosix::GnubbyAuthHandlerPosix( | |
68 protocol::ClientStub* client_stub) | |
69 : client_stub_(client_stub), last_connection_id_(0) { | |
70 DCHECK(client_stub_); | |
71 } | |
72 | |
73 GnubbyAuthHandlerPosix::~GnubbyAuthHandlerPosix() { | |
74 STLDeleteValues(&active_sockets_); | |
75 } | |
76 | |
77 // static | |
78 scoped_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( | |
79 protocol::ClientStub* client_stub) { | |
80 return scoped_ptr<GnubbyAuthHandler>(new GnubbyAuthHandlerPosix(client_stub)); | |
81 } | |
82 | |
83 // static | |
84 void GnubbyAuthHandler::SetGnubbySocketName( | |
85 const base::FilePath& gnubby_socket_name) { | |
86 g_gnubby_socket_name.Get() = gnubby_socket_name; | |
87 } | |
88 | |
89 void GnubbyAuthHandlerPosix::DeliverClientMessage(const std::string& message) { | |
90 DCHECK(CalledOnValidThread()); | |
91 | |
92 std::vector<std::string> values; | |
93 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.
| |
94 | |
95 if (values[0] == "control") { | |
96 if (values[1] == "auth-v1") { | |
97 CreateAuthorizationSocket(); | |
98 } | |
99 } else if (values[0] == "data") { | |
100 int connection_id = 0; | |
101 base::StringToInt(values[1], &connection_id); | |
102 | |
103 ActiveSockets::iterator iter = active_sockets_.find(connection_id); | |
104 if (iter != active_sockets_.end()) { | |
105 HOST_LOG << "Sending gnubby response"; | |
106 | |
107 std::string reply; | |
108 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
| |
109 | |
110 iter->second->Send(reply); | |
111 } | |
112 } | |
113 } | |
114 | |
115 void GnubbyAuthHandlerPosix::DeliverHostDataMessage(int connection_id, | |
116 const std::string& data) | |
117 const { | |
118 DCHECK(CalledOnValidThread()); | |
119 | |
120 protocol::ExtensionMessage message; | |
121 message.set_type("gnubby-auth"); | |
122 message.set_data("data " + base::IntToString(connection_id) + " " + data); | |
123 | |
124 client_stub_->DeliverHostMessage(message); | |
125 } | |
126 | |
127 bool GnubbyAuthHandlerPosix::HasActiveSocketForTesting( | |
128 net::StreamListenSocket* socket) const { | |
129 return std::find_if(active_sockets_.begin(), | |
130 active_sockets_.end(), | |
131 CompareSocket(socket)) != active_sockets_.end(); | |
132 } | |
133 | |
134 void GnubbyAuthHandlerPosix::DidAccept( | |
135 net::StreamListenSocket* server, | |
136 scoped_ptr<net::StreamListenSocket> socket) { | |
137 DCHECK(CalledOnValidThread()); | |
138 | |
139 active_sockets_[++last_connection_id_] = socket.release(); | |
140 } | |
141 | |
142 void GnubbyAuthHandlerPosix::DidRead(net::StreamListenSocket* socket, | |
143 const char* data, | |
144 int len) { | |
145 DCHECK(CalledOnValidThread()); | |
146 | |
147 ActiveSockets::iterator socket_iter = std::find_if( | |
148 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); | |
149 if (socket_iter != active_sockets_.end()) { | |
150 int connection_id = socket_iter->first; | |
151 | |
152 ActiveRequests::iterator request_iter = | |
153 active_requests_.find(connection_id); | |
154 if (request_iter != active_requests_.end()) { | |
155 std::vector<char>& saved_vector = request_iter->second; | |
156 saved_vector.insert(saved_vector.end(), data, data + len); | |
157 | |
158 if (IsRequestComplete(saved_vector.data(), saved_vector.size())) { | |
159 ProcessGnubbyRequest( | |
160 connection_id, saved_vector.data(), saved_vector.size()); | |
161 active_requests_.erase(request_iter); | |
162 } | |
163 } else if (IsRequestComplete(data, len)) { | |
164 ProcessGnubbyRequest(connection_id, data, len); | |
165 } else { | |
166 active_requests_[connection_id] = std::vector<char>(data, data + len); | |
167 } | |
168 } | |
169 } | |
170 | |
171 void GnubbyAuthHandlerPosix::DidClose(net::StreamListenSocket* socket) { | |
172 DCHECK(CalledOnValidThread()); | |
173 | |
174 ActiveSockets::iterator iter = std::find_if( | |
175 active_sockets_.begin(), active_sockets_.end(), CompareSocket(socket)); | |
176 if (iter != active_sockets_.end()) { | |
177 active_requests_.erase(iter->first); | |
178 | |
179 delete iter->second; | |
180 active_sockets_.erase(iter); | |
181 } | |
182 } | |
183 | |
184 void GnubbyAuthHandlerPosix::CreateAuthorizationSocket() { | |
185 DCHECK(CalledOnValidThread()); | |
186 | |
187 if (!g_gnubby_socket_name.Get().empty()) { | |
188 // If the file already exists, a socket in use error is returned. | |
189 base::DeleteFile(g_gnubby_socket_name.Get(), false); | |
190 | |
191 HOST_LOG << "Listening for gnubby requests on " | |
192 << g_gnubby_socket_name.Get().value(); | |
193 | |
194 auth_socket_ = net::UnixDomainSocket::CreateAndListen( | |
195 g_gnubby_socket_name.Get().value(), this, base::Bind(MatchUid)); | |
196 if (!auth_socket_.get()) { | |
197 LOG(ERROR) << "Failed to open socket for gnubby requests"; | |
198 } | |
199 } else { | |
200 HOST_LOG << "No gnubby socket name specified"; | |
201 } | |
202 } | |
203 | |
204 void GnubbyAuthHandlerPosix::ProcessGnubbyRequest(int connection_id, | |
205 const char* data, | |
206 int data_len) { | |
207 std::string json; | |
208 if (GetJsonFromGnubbyRequest(data, data_len, &json)) { | |
209 HOST_LOG << "Received gnubby request"; | |
210 DeliverHostDataMessage(connection_id, json); | |
211 } else { | |
212 LOG(ERROR) << "Could not decode gnubby request"; | |
213 } | |
214 } | |
215 | |
216 } // namespace remoting | |
OLD | NEW |