OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/security_key/gnubby_auth_handler.h" | 5 #include "remoting/host/security_key/security_key_auth_handler.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <unistd.h> | 8 #include <unistd.h> |
9 | 9 |
10 #include <memory> | 10 #include <memory> |
11 | 11 |
12 #include "base/bind.h" | 12 #include "base/bind.h" |
13 #include "base/files/file_util.h" | 13 #include "base/files/file_util.h" |
14 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
17 #include "base/threading/thread_checker.h" | 17 #include "base/threading/thread_checker.h" |
18 #include "base/threading/thread_restrictions.h" | 18 #include "base/threading/thread_restrictions.h" |
19 #include "base/values.h" | 19 #include "base/values.h" |
20 #include "net/base/completion_callback.h" | 20 #include "net/base/completion_callback.h" |
21 #include "net/base/net_errors.h" | 21 #include "net/base/net_errors.h" |
22 #include "net/socket/stream_socket.h" | 22 #include "net/socket/stream_socket.h" |
23 #include "net/socket/unix_domain_server_socket_posix.h" | 23 #include "net/socket/unix_domain_server_socket_posix.h" |
24 #include "remoting/base/logging.h" | 24 #include "remoting/base/logging.h" |
25 #include "remoting/host/security_key/gnubby_socket.h" | 25 #include "remoting/host/security_key/security_key_socket.h" |
26 | 26 |
27 namespace { | 27 namespace { |
28 | 28 |
29 const int64_t kDefaultRequestTimeoutSeconds = 60; | 29 const int64_t kDefaultRequestTimeoutSeconds = 60; |
30 | 30 |
31 // The name of the socket to listen for gnubby requests on. | 31 // The name of the socket to listen for security key requests on. |
32 base::LazyInstance<base::FilePath>::Leaky g_gnubby_socket_name = | 32 base::LazyInstance<base::FilePath>::Leaky g_security_key_socket_name = |
33 LAZY_INSTANCE_INITIALIZER; | 33 LAZY_INSTANCE_INITIALIZER; |
34 | 34 |
35 // Socket authentication function that only allows connections from callers with | 35 // Socket authentication function that only allows connections from callers with |
36 // the current uid. | 36 // the current uid. |
37 bool MatchUid(const net::UnixDomainServerSocket::Credentials& credentials) { | 37 bool MatchUid(const net::UnixDomainServerSocket::Credentials& credentials) { |
38 bool allowed = credentials.user_id == getuid(); | 38 bool allowed = credentials.user_id == getuid(); |
39 if (!allowed) | 39 if (!allowed) |
40 HOST_LOG << "Refused socket connection from uid " << credentials.user_id; | 40 HOST_LOG << "Refused socket connection from uid " << credentials.user_id; |
41 return allowed; | 41 return allowed; |
42 } | 42 } |
43 | 43 |
44 // Returns the command code (the first byte of the data) if it exists, or -1 if | 44 // Returns the command code (the first byte of the data) if it exists, or -1 if |
45 // the data is empty. | 45 // the data is empty. |
46 unsigned int GetCommandCode(const std::string& data) { | 46 unsigned int GetCommandCode(const std::string& data) { |
47 return data.empty() ? -1 : static_cast<unsigned int>(data[0]); | 47 return data.empty() ? -1 : static_cast<unsigned int>(data[0]); |
48 } | 48 } |
49 | 49 |
50 } // namespace | 50 } // namespace |
51 | 51 |
52 namespace remoting { | 52 namespace remoting { |
53 | 53 |
54 class GnubbyAuthHandlerLinux : public GnubbyAuthHandler { | 54 class SecurityKeyAuthHandlerLinux : public SecurityKeyAuthHandler { |
55 public: | 55 public: |
56 GnubbyAuthHandlerLinux(); | 56 SecurityKeyAuthHandlerLinux(); |
57 ~GnubbyAuthHandlerLinux() override; | 57 ~SecurityKeyAuthHandlerLinux() override; |
58 | 58 |
59 private: | 59 private: |
60 typedef std::map<int, GnubbySocket*> ActiveSockets; | 60 typedef std::map<int, SecurityKeySocket*> ActiveSockets; |
61 | 61 |
62 // GnubbyAuthHandler interface. | 62 // SecurityKeyAuthHandler interface. |
63 void CreateGnubbyConnection() override; | 63 void CreateSecurityKeyConnection() override; |
64 bool IsValidConnectionId(int gnubby_connection_id) const override; | 64 bool IsValidConnectionId(int security_key_connection_id) const override; |
65 void SendClientResponse(int gnubby_connection_id, | 65 void SendClientResponse(int security_key_connection_id, |
66 const std::string& response) override; | 66 const std::string& response) override; |
67 void SendErrorAndCloseConnection(int gnubby_connection_id) override; | 67 void SendErrorAndCloseConnection(int security_key_connection_id) override; |
68 void SetSendMessageCallback(const SendMessageCallback& callback) override; | 68 void SetSendMessageCallback(const SendMessageCallback& callback) override; |
69 size_t GetActiveConnectionCountForTest() const override; | 69 size_t GetActiveConnectionCountForTest() const override; |
70 void SetRequestTimeoutForTest(base::TimeDelta timeout) override; | 70 void SetRequestTimeoutForTest(base::TimeDelta timeout) override; |
71 | 71 |
72 // Starts listening for connection. | 72 // Starts listening for connection. |
73 void DoAccept(); | 73 void DoAccept(); |
74 | 74 |
75 // Called when a connection is accepted. | 75 // Called when a connection is accepted. |
76 void OnAccepted(int result); | 76 void OnAccepted(int result); |
77 | 77 |
78 // Called when a GnubbySocket has done reading. | 78 // Called when a SecurityKeySocket has done reading. |
79 void OnReadComplete(int gnubby_connection_id); | 79 void OnReadComplete(int security_key_connection_id); |
80 | 80 |
81 // Gets an active socket iterator for |gnubby_connection_id|. | 81 // Gets an active socket iterator for |security_key_connection_id|. |
82 ActiveSockets::const_iterator GetSocketForConnectionId( | 82 ActiveSockets::const_iterator GetSocketForConnectionId( |
83 int gnubby_connection_id) const; | 83 int security_key_connection_id) const; |
84 | 84 |
85 // Send an error and closes an active socket. | 85 // Send an error and closes an active socket. |
86 void SendErrorAndCloseActiveSocket(const ActiveSockets::const_iterator& iter); | 86 void SendErrorAndCloseActiveSocket(const ActiveSockets::const_iterator& iter); |
87 | 87 |
88 // A request timed out. | 88 // A request timed out. |
89 void RequestTimedOut(int gnubby_connection_id); | 89 void RequestTimedOut(int security_key_connection_id); |
90 | 90 |
91 // Ensures GnubbyAuthHandlerLinux methods are called on the same thread. | 91 // Ensures SecurityKeyAuthHandlerLinux methods are called on the same thread. |
92 base::ThreadChecker thread_checker_; | 92 base::ThreadChecker thread_checker_; |
93 | 93 |
94 // Socket used to listen for authorization requests. | 94 // Socket used to listen for authorization requests. |
95 std::unique_ptr<net::UnixDomainServerSocket> auth_socket_; | 95 std::unique_ptr<net::UnixDomainServerSocket> auth_socket_; |
96 | 96 |
97 // A temporary holder for an accepted connection. | 97 // A temporary holder for an accepted connection. |
98 std::unique_ptr<net::StreamSocket> accept_socket_; | 98 std::unique_ptr<net::StreamSocket> accept_socket_; |
99 | 99 |
100 // Used to pass gnubby extension messages to the client. | 100 // Used to pass security key extension messages to the client. |
101 SendMessageCallback send_message_callback_; | 101 SendMessageCallback send_message_callback_; |
102 | 102 |
103 // The last assigned gnubby connection id. | 103 // The last assigned security key connection id. |
104 int last_connection_id_; | 104 int last_connection_id_; |
105 | 105 |
106 // Sockets by connection id used to process gnubbyd requests. | 106 // Sockets by connection id used to process gnubbyd requests. |
107 ActiveSockets active_sockets_; | 107 ActiveSockets active_sockets_; |
108 | 108 |
109 // Timeout used for a request. | 109 // Timeout used for a request. |
110 base::TimeDelta request_timeout_; | 110 base::TimeDelta request_timeout_; |
111 | 111 |
112 DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerLinux); | 112 DISALLOW_COPY_AND_ASSIGN(SecurityKeyAuthHandlerLinux); |
113 }; | 113 }; |
114 | 114 |
115 std::unique_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( | 115 std::unique_ptr<SecurityKeyAuthHandler> SecurityKeyAuthHandler::Create( |
116 ClientSessionDetails* client_session_details, | 116 ClientSessionDetails* client_session_details, |
117 const SendMessageCallback& send_message_callback) { | 117 const SendMessageCallback& send_message_callback) { |
118 std::unique_ptr<GnubbyAuthHandler> auth_handler(new GnubbyAuthHandlerLinux()); | 118 std::unique_ptr<SecurityKeyAuthHandler> auth_handler( |
| 119 new SecurityKeyAuthHandlerLinux()); |
119 auth_handler->SetSendMessageCallback(send_message_callback); | 120 auth_handler->SetSendMessageCallback(send_message_callback); |
120 return auth_handler; | 121 return auth_handler; |
121 } | 122 } |
122 | 123 |
123 void GnubbyAuthHandler::SetGnubbySocketName( | 124 void SecurityKeyAuthHandler::SetSecurityKeySocketName( |
124 const base::FilePath& gnubby_socket_name) { | 125 const base::FilePath& security_key_socket_name) { |
125 g_gnubby_socket_name.Get() = gnubby_socket_name; | 126 g_security_key_socket_name.Get() = security_key_socket_name; |
126 } | 127 } |
127 | 128 |
128 GnubbyAuthHandlerLinux::GnubbyAuthHandlerLinux() | 129 SecurityKeyAuthHandlerLinux::SecurityKeyAuthHandlerLinux() |
129 : last_connection_id_(0), | 130 : last_connection_id_(0), |
130 request_timeout_( | 131 request_timeout_( |
131 base::TimeDelta::FromSeconds(kDefaultRequestTimeoutSeconds)) {} | 132 base::TimeDelta::FromSeconds(kDefaultRequestTimeoutSeconds)) {} |
132 | 133 |
133 GnubbyAuthHandlerLinux::~GnubbyAuthHandlerLinux() { | 134 SecurityKeyAuthHandlerLinux::~SecurityKeyAuthHandlerLinux() { |
134 STLDeleteValues(&active_sockets_); | 135 STLDeleteValues(&active_sockets_); |
135 } | 136 } |
136 | 137 |
137 void GnubbyAuthHandlerLinux::CreateGnubbyConnection() { | 138 void SecurityKeyAuthHandlerLinux::CreateSecurityKeyConnection() { |
138 DCHECK(thread_checker_.CalledOnValidThread()); | 139 DCHECK(thread_checker_.CalledOnValidThread()); |
139 DCHECK(!g_gnubby_socket_name.Get().empty()); | 140 DCHECK(!g_security_key_socket_name.Get().empty()); |
140 | 141 |
141 { | 142 { |
142 // DeleteFile() is a blocking operation, but so is creation of the unix | 143 // DeleteFile() is a blocking operation, but so is creation of the unix |
143 // socket below. Consider moving this class to a different thread if this | 144 // socket below. Consider moving this class to a different thread if this |
144 // causes any problems. See crbug.com/509807. | 145 // causes any problems. See crbug.com/509807. |
145 // TODO(joedow): Since this code now runs as a host extension, we should | 146 // TODO(joedow): Since this code now runs as a host extension, we should |
146 // perform our IO on a separate thread: crbug.com/591739 | 147 // perform our IO on a separate thread: crbug.com/591739 |
147 base::ThreadRestrictions::ScopedAllowIO allow_io; | 148 base::ThreadRestrictions::ScopedAllowIO allow_io; |
148 | 149 |
149 // If the file already exists, a socket in use error is returned. | 150 // If the file already exists, a socket in use error is returned. |
150 base::DeleteFile(g_gnubby_socket_name.Get(), false); | 151 base::DeleteFile(g_security_key_socket_name.Get(), false); |
151 } | 152 } |
152 | 153 |
153 HOST_LOG << "Listening for gnubby requests on " | 154 HOST_LOG << "Listening for security key requests on " |
154 << g_gnubby_socket_name.Get().value(); | 155 << g_security_key_socket_name.Get().value(); |
155 | 156 |
156 auth_socket_.reset( | 157 auth_socket_.reset( |
157 new net::UnixDomainServerSocket(base::Bind(MatchUid), false)); | 158 new net::UnixDomainServerSocket(base::Bind(MatchUid), false)); |
158 int rv = auth_socket_->BindAndListen(g_gnubby_socket_name.Get().value(), | 159 int rv = auth_socket_->BindAndListen(g_security_key_socket_name.Get().value(), |
159 /*backlog=*/1); | 160 /*backlog=*/1); |
160 if (rv != net::OK) { | 161 if (rv != net::OK) { |
161 LOG(ERROR) << "Failed to open socket for gnubby requests: '" << rv << "'"; | 162 LOG(ERROR) << "Failed to open socket for auth requests: '" << rv << "'"; |
162 return; | 163 return; |
163 } | 164 } |
164 DoAccept(); | 165 DoAccept(); |
165 } | 166 } |
166 | 167 |
167 bool GnubbyAuthHandlerLinux::IsValidConnectionId( | 168 bool SecurityKeyAuthHandlerLinux::IsValidConnectionId( |
168 int gnubby_connection_id) const { | 169 int security_key_connection_id) const { |
169 return GetSocketForConnectionId(gnubby_connection_id) != | 170 return GetSocketForConnectionId(security_key_connection_id) != |
170 active_sockets_.end(); | 171 active_sockets_.end(); |
171 } | 172 } |
172 | 173 |
173 void GnubbyAuthHandlerLinux::SendClientResponse(int gnubby_connection_id, | 174 void SecurityKeyAuthHandlerLinux::SendClientResponse( |
174 const std::string& response) { | 175 int security_key_connection_id, |
| 176 const std::string& response) { |
175 ActiveSockets::const_iterator iter = | 177 ActiveSockets::const_iterator iter = |
176 GetSocketForConnectionId(gnubby_connection_id); | 178 GetSocketForConnectionId(security_key_connection_id); |
177 if (iter != active_sockets_.end()) { | 179 if (iter != active_sockets_.end()) { |
178 iter->second->SendResponse(response); | 180 iter->second->SendResponse(response); |
179 } else { | 181 } else { |
180 LOG(WARNING) << "Unknown gnubby-auth data connection: '" | 182 LOG(WARNING) << "Unknown gnubby-auth data connection: '" |
181 << gnubby_connection_id << "'"; | 183 << security_key_connection_id << "'"; |
182 } | 184 } |
183 } | 185 } |
184 | 186 |
185 void GnubbyAuthHandlerLinux::SendErrorAndCloseConnection( | 187 void SecurityKeyAuthHandlerLinux::SendErrorAndCloseConnection( |
186 int gnubby_connection_id) { | 188 int security_key_connection_id) { |
187 ActiveSockets::const_iterator iter = | 189 ActiveSockets::const_iterator iter = |
188 GetSocketForConnectionId(gnubby_connection_id); | 190 GetSocketForConnectionId(security_key_connection_id); |
189 if (iter != active_sockets_.end()) { | 191 if (iter != active_sockets_.end()) { |
190 HOST_LOG << "Sending gnubby error"; | 192 HOST_LOG << "Sending security key error"; |
191 SendErrorAndCloseActiveSocket(iter); | 193 SendErrorAndCloseActiveSocket(iter); |
192 } else { | 194 } else { |
193 LOG(WARNING) << "Unknown gnubby-auth data connection: '" | 195 LOG(WARNING) << "Unknown gnubby-auth data connection: '" |
194 << gnubby_connection_id << "'"; | 196 << security_key_connection_id << "'"; |
195 } | 197 } |
196 } | 198 } |
197 | 199 |
198 void GnubbyAuthHandlerLinux::SetSendMessageCallback( | 200 void SecurityKeyAuthHandlerLinux::SetSendMessageCallback( |
199 const SendMessageCallback& callback) { | 201 const SendMessageCallback& callback) { |
200 send_message_callback_ = callback; | 202 send_message_callback_ = callback; |
201 } | 203 } |
202 | 204 |
203 size_t GnubbyAuthHandlerLinux::GetActiveConnectionCountForTest() const { | 205 size_t SecurityKeyAuthHandlerLinux::GetActiveConnectionCountForTest() const { |
204 return active_sockets_.size(); | 206 return active_sockets_.size(); |
205 } | 207 } |
206 | 208 |
207 void GnubbyAuthHandlerLinux::SetRequestTimeoutForTest(base::TimeDelta timeout) { | 209 void SecurityKeyAuthHandlerLinux::SetRequestTimeoutForTest( |
| 210 base::TimeDelta timeout) { |
208 request_timeout_ = timeout; | 211 request_timeout_ = timeout; |
209 } | 212 } |
210 | 213 |
211 void GnubbyAuthHandlerLinux::DoAccept() { | 214 void SecurityKeyAuthHandlerLinux::DoAccept() { |
212 int result = auth_socket_->Accept( | 215 int result = auth_socket_->Accept( |
213 &accept_socket_, | 216 &accept_socket_, base::Bind(&SecurityKeyAuthHandlerLinux::OnAccepted, |
214 base::Bind(&GnubbyAuthHandlerLinux::OnAccepted, base::Unretained(this))); | 217 base::Unretained(this))); |
215 if (result != net::ERR_IO_PENDING) | 218 if (result != net::ERR_IO_PENDING) |
216 OnAccepted(result); | 219 OnAccepted(result); |
217 } | 220 } |
218 | 221 |
219 void GnubbyAuthHandlerLinux::OnAccepted(int result) { | 222 void SecurityKeyAuthHandlerLinux::OnAccepted(int result) { |
220 DCHECK(thread_checker_.CalledOnValidThread()); | 223 DCHECK(thread_checker_.CalledOnValidThread()); |
221 DCHECK_NE(net::ERR_IO_PENDING, result); | 224 DCHECK_NE(net::ERR_IO_PENDING, result); |
222 | 225 |
223 if (result < 0) { | 226 if (result < 0) { |
224 LOG(ERROR) << "Error in accepting a new connection"; | 227 LOG(ERROR) << "Error in accepting a new connection"; |
225 return; | 228 return; |
226 } | 229 } |
227 | 230 |
228 int gnubby_connection_id = ++last_connection_id_; | 231 int security_key_connection_id = ++last_connection_id_; |
229 GnubbySocket* socket = new GnubbySocket( | 232 SecurityKeySocket* socket = new SecurityKeySocket( |
230 std::move(accept_socket_), request_timeout_, | 233 std::move(accept_socket_), request_timeout_, |
231 base::Bind(&GnubbyAuthHandlerLinux::RequestTimedOut, | 234 base::Bind(&SecurityKeyAuthHandlerLinux::RequestTimedOut, |
232 base::Unretained(this), gnubby_connection_id)); | 235 base::Unretained(this), security_key_connection_id)); |
233 active_sockets_[gnubby_connection_id] = socket; | 236 active_sockets_[security_key_connection_id] = socket; |
234 socket->StartReadingRequest( | 237 socket->StartReadingRequest( |
235 base::Bind(&GnubbyAuthHandlerLinux::OnReadComplete, | 238 base::Bind(&SecurityKeyAuthHandlerLinux::OnReadComplete, |
236 base::Unretained(this), gnubby_connection_id)); | 239 base::Unretained(this), security_key_connection_id)); |
237 | 240 |
238 // Continue accepting new connections. | 241 // Continue accepting new connections. |
239 DoAccept(); | 242 DoAccept(); |
240 } | 243 } |
241 | 244 |
242 void GnubbyAuthHandlerLinux::OnReadComplete(int gnubby_connection_id) { | 245 void SecurityKeyAuthHandlerLinux::OnReadComplete( |
| 246 int security_key_connection_id) { |
243 DCHECK(thread_checker_.CalledOnValidThread()); | 247 DCHECK(thread_checker_.CalledOnValidThread()); |
244 | 248 |
245 ActiveSockets::const_iterator iter = | 249 ActiveSockets::const_iterator iter = |
246 active_sockets_.find(gnubby_connection_id); | 250 active_sockets_.find(security_key_connection_id); |
247 DCHECK(iter != active_sockets_.end()); | 251 DCHECK(iter != active_sockets_.end()); |
248 std::string request_data; | 252 std::string request_data; |
249 if (!iter->second->GetAndClearRequestData(&request_data)) { | 253 if (!iter->second->GetAndClearRequestData(&request_data)) { |
250 SendErrorAndCloseActiveSocket(iter); | 254 SendErrorAndCloseActiveSocket(iter); |
251 return; | 255 return; |
252 } | 256 } |
253 | 257 |
254 HOST_LOG << "Received gnubby request: " << GetCommandCode(request_data); | 258 HOST_LOG << "Received security key request: " << GetCommandCode(request_data); |
255 send_message_callback_.Run(gnubby_connection_id, request_data); | 259 send_message_callback_.Run(security_key_connection_id, request_data); |
256 | 260 |
257 iter->second->StartReadingRequest( | 261 iter->second->StartReadingRequest( |
258 base::Bind(&GnubbyAuthHandlerLinux::OnReadComplete, | 262 base::Bind(&SecurityKeyAuthHandlerLinux::OnReadComplete, |
259 base::Unretained(this), gnubby_connection_id)); | 263 base::Unretained(this), security_key_connection_id)); |
260 } | 264 } |
261 | 265 |
262 GnubbyAuthHandlerLinux::ActiveSockets::const_iterator | 266 SecurityKeyAuthHandlerLinux::ActiveSockets::const_iterator |
263 GnubbyAuthHandlerLinux::GetSocketForConnectionId( | 267 SecurityKeyAuthHandlerLinux::GetSocketForConnectionId( |
264 int gnubby_connection_id) const { | 268 int security_key_connection_id) const { |
265 return active_sockets_.find(gnubby_connection_id); | 269 return active_sockets_.find(security_key_connection_id); |
266 } | 270 } |
267 | 271 |
268 void GnubbyAuthHandlerLinux::SendErrorAndCloseActiveSocket( | 272 void SecurityKeyAuthHandlerLinux::SendErrorAndCloseActiveSocket( |
269 const ActiveSockets::const_iterator& iter) { | 273 const ActiveSockets::const_iterator& iter) { |
270 iter->second->SendSshError(); | 274 iter->second->SendSshError(); |
271 delete iter->second; | 275 delete iter->second; |
272 active_sockets_.erase(iter); | 276 active_sockets_.erase(iter); |
273 } | 277 } |
274 | 278 |
275 void GnubbyAuthHandlerLinux::RequestTimedOut(int gnubby_connection_id) { | 279 void SecurityKeyAuthHandlerLinux::RequestTimedOut( |
276 HOST_LOG << "Gnubby request timed out"; | 280 int security_key_connection_id) { |
| 281 HOST_LOG << "SecurityKey request timed out"; |
277 ActiveSockets::const_iterator iter = | 282 ActiveSockets::const_iterator iter = |
278 active_sockets_.find(gnubby_connection_id); | 283 active_sockets_.find(security_key_connection_id); |
279 if (iter != active_sockets_.end()) | 284 if (iter != active_sockets_.end()) |
280 SendErrorAndCloseActiveSocket(iter); | 285 SendErrorAndCloseActiveSocket(iter); |
281 } | 286 } |
282 | 287 |
283 } // namespace remoting | 288 } // namespace remoting |
OLD | NEW |