OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "remoting/host/security_key/gnubby_auth_handler.h" | |
6 | |
7 #include <cstdint> | |
8 #include <map> | |
9 #include <memory> | |
10 #include <string> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/location.h" | |
14 #include "base/logging.h" | |
15 #include "base/memory/weak_ptr.h" | |
16 #include "base/strings/stringprintf.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "base/threading/thread_checker.h" | |
19 #include "base/threading/thread_task_runner_handle.h" | |
20 #include "base/time/time.h" | |
21 #include "base/timer/timer.h" | |
22 #include "base/win/win_util.h" | |
23 #include "ipc/ipc_channel.h" | |
24 #include "ipc/ipc_listener.h" | |
25 #include "ipc/ipc_message.h" | |
26 #include "ipc/ipc_message_macros.h" | |
27 #include "remoting/base/logging.h" | |
28 #include "remoting/host/chromoting_messages.h" | |
29 #include "remoting/host/client_session_details.h" | |
30 #include "remoting/host/ipc_util.h" | |
31 #include "remoting/host/security_key/remote_security_key_ipc_constants.h" | |
32 #include "remoting/host/security_key/remote_security_key_ipc_server.h" | |
33 | |
34 namespace { | |
35 | |
36 // The timeout used to disconnect a client from the IPC Server channel if it | |
37 // forgets to do so. This ensures the server channel is not blocked forever. | |
38 const int kInitialRequestTimeoutSeconds = 5; | |
39 | |
40 // This value represents the amount of time to wait for a gnubby request from | |
41 // the client before terminating the connection. | |
42 const int kGnubbyRequestTimeoutSeconds = 60; | |
43 | |
44 } // namespace | |
45 | |
46 namespace remoting { | |
47 | |
48 // Creates an IPC server channel which services IPC clients that want to start | |
49 // a security key forwarding session. Once an IPC Client connects to the | |
50 // server, the GnubbyAuthHandlerWin class will create a new | |
51 // RemoteSecurityKeyIpcServer instance that will service that request. The new | |
52 // instance will exist for the lifetime of the security key request and will be | |
53 // assigned a unique IPC channel name and connection id. The channel name is | |
54 // sent to the client which should disconnect the IPC server channel and | |
55 // connect to the security key forwarding session IPC channel to send/receive | |
56 // security key messages. The IPC server channel will then be reset so it can | |
57 // can service the next client/request. This system allows multiple security | |
58 // key forwarding sessions to occur concurrently. | |
59 // TODO(joedow): Update GnubbyAuthHandler impls to run on a separate IO thread | |
60 // instead of the thread it was created on: crbug.com/591739 | |
61 class GnubbyAuthHandlerWin : public GnubbyAuthHandler, public IPC::Listener { | |
62 public: | |
63 explicit GnubbyAuthHandlerWin(ClientSessionDetails* client_session_details); | |
64 ~GnubbyAuthHandlerWin() override; | |
65 | |
66 private: | |
67 typedef std::map<int, std::unique_ptr<RemoteSecurityKeyIpcServer>> | |
68 ActiveChannels; | |
69 | |
70 // GnubbyAuthHandler interface. | |
71 void CreateGnubbyConnection() override; | |
72 bool IsValidConnectionId(int gnubby_connection_id) const override; | |
73 void SendClientResponse(int gnubby_connection_id, | |
74 const std::string& response) override; | |
75 void SendErrorAndCloseConnection(int gnubby_connection_id) override; | |
76 void SetSendMessageCallback(const SendMessageCallback& callback) override; | |
77 size_t GetActiveConnectionCountForTest() const override; | |
78 void SetRequestTimeoutForTest(base::TimeDelta timeout) override; | |
79 | |
80 // IPC::Listener implementation. | |
81 bool OnMessageReceived(const IPC::Message& message) override; | |
82 void OnChannelConnected(int32_t peer_pid) override; | |
83 void OnChannelError() override; | |
84 | |
85 // Creates the IPC server channel and waits for a connection using | |
86 // |ipc_server_channel_name_|. | |
87 void StartIpcServerChannel(); | |
88 | |
89 // Restarts the IPC server channel to prepare for another connection. | |
90 void RecreateIpcServerChannel(); | |
91 | |
92 // Closes the IPC channel created for a security key forwarding session. | |
93 void CloseSecurityKeyRequestIpcChannel(int connection_id); | |
94 | |
95 // Returns the IPC Channel instance created for |connection_id|. | |
96 ActiveChannels::const_iterator GetChannelForConnectionId( | |
97 int connection_id) const; | |
98 | |
99 // Creates a unique name based on the well-known IPC channel name. | |
100 std::string GenerateUniqueChannelName(); | |
101 | |
102 // Represents the last id assigned to a new security key request IPC channel. | |
103 int last_connection_id_ = 0; | |
104 | |
105 // Sends a gnubby extension messages to the remote client when called. | |
106 SendMessageCallback send_message_callback_; | |
107 | |
108 // Interface which provides details about the client session. | |
109 ClientSessionDetails* client_session_details_ = nullptr; | |
110 | |
111 // Tracks the IPC channel created for each security key forwarding session. | |
112 ActiveChannels active_channels_; | |
113 | |
114 // The amount of time to wait for a client to process the connection details | |
115 // message and disconnect from the IPC server channel before disconnecting it. | |
116 base::TimeDelta disconnect_timeout_; | |
117 | |
118 // Used to recreate the IPC server channel if a client forgets to disconnect. | |
119 base::OneShotTimer timer_; | |
120 | |
121 // IPC Clients connect to this channel first to receive their own IPC | |
122 // channel to start a security key forwarding session on. | |
123 std::unique_ptr<IPC::Channel> ipc_server_channel_; | |
124 | |
125 // Ensures GnubbyAuthHandlerWin methods are called on the same thread. | |
126 base::ThreadChecker thread_checker_; | |
127 | |
128 base::WeakPtrFactory<GnubbyAuthHandlerWin> weak_factory_; | |
129 | |
130 DISALLOW_COPY_AND_ASSIGN(GnubbyAuthHandlerWin); | |
131 }; | |
132 | |
133 std::unique_ptr<GnubbyAuthHandler> GnubbyAuthHandler::Create( | |
134 ClientSessionDetails* client_session_details, | |
135 const SendMessageCallback& send_message_callback) { | |
136 std::unique_ptr<GnubbyAuthHandler> auth_handler( | |
137 new GnubbyAuthHandlerWin(client_session_details)); | |
138 auth_handler->SetSendMessageCallback(send_message_callback); | |
139 return auth_handler; | |
140 } | |
141 | |
142 GnubbyAuthHandlerWin::GnubbyAuthHandlerWin( | |
143 ClientSessionDetails* client_session_details) | |
144 : client_session_details_(client_session_details), | |
145 disconnect_timeout_( | |
146 base::TimeDelta::FromSeconds(kInitialRequestTimeoutSeconds)), | |
147 weak_factory_(this) { | |
148 DCHECK(client_session_details_); | |
149 } | |
150 | |
151 GnubbyAuthHandlerWin::~GnubbyAuthHandlerWin() {} | |
152 | |
153 void GnubbyAuthHandlerWin::CreateGnubbyConnection() { | |
154 DCHECK(thread_checker_.CalledOnValidThread()); | |
155 StartIpcServerChannel(); | |
156 } | |
157 | |
158 bool GnubbyAuthHandlerWin::IsValidConnectionId(int connection_id) const { | |
159 DCHECK(thread_checker_.CalledOnValidThread()); | |
160 return (GetChannelForConnectionId(connection_id) != active_channels_.end()); | |
161 } | |
162 | |
163 void GnubbyAuthHandlerWin::SendClientResponse( | |
164 int connection_id, | |
165 const std::string& response_data) { | |
166 DCHECK(thread_checker_.CalledOnValidThread()); | |
167 | |
168 ActiveChannels::const_iterator iter = | |
169 GetChannelForConnectionId(connection_id); | |
170 if (iter == active_channels_.end()) { | |
171 HOST_LOG << "Invalid gnubby connection ID received: " << connection_id; | |
172 return; | |
173 } | |
174 | |
175 if (!iter->second->SendResponse(response_data)) { | |
176 CloseSecurityKeyRequestIpcChannel(connection_id); | |
177 } | |
178 } | |
179 | |
180 void GnubbyAuthHandlerWin::SendErrorAndCloseConnection(int connection_id) { | |
181 DCHECK(thread_checker_.CalledOnValidThread()); | |
182 | |
183 SendClientResponse(connection_id, kRemoteSecurityKeyConnectionError); | |
184 CloseSecurityKeyRequestIpcChannel(connection_id); | |
185 } | |
186 | |
187 void GnubbyAuthHandlerWin::SetSendMessageCallback( | |
188 const SendMessageCallback& callback) { | |
189 DCHECK(thread_checker_.CalledOnValidThread()); | |
190 send_message_callback_ = callback; | |
191 } | |
192 | |
193 size_t GnubbyAuthHandlerWin::GetActiveConnectionCountForTest() const { | |
194 return active_channels_.size(); | |
195 } | |
196 | |
197 void GnubbyAuthHandlerWin::SetRequestTimeoutForTest(base::TimeDelta timeout) { | |
198 disconnect_timeout_ = timeout; | |
199 } | |
200 | |
201 void GnubbyAuthHandlerWin::StartIpcServerChannel() { | |
202 DCHECK(thread_checker_.CalledOnValidThread()); | |
203 | |
204 // Create a named pipe owned by the current user (the LocalService account | |
205 // (SID: S-1-5-19) when running in the network process) which is available to | |
206 // all authenticated users. | |
207 // presubmit: allow wstring | |
208 std::wstring user_sid; | |
209 CHECK(base::win::GetUserSidString(&user_sid)); | |
210 std::string user_sid_utf8 = base::WideToUTF8(user_sid); | |
211 std::string security_descriptor = base::StringPrintf( | |
212 "O:%sG:%sD:(A;;GA;;;AU)", user_sid_utf8.c_str(), user_sid_utf8.c_str()); | |
213 | |
214 base::win::ScopedHandle pipe; | |
215 CHECK(CreateIpcChannel(remoting::GetRemoteSecurityKeyIpcChannelName(), | |
216 security_descriptor, &pipe)); | |
217 ipc_server_channel_ = | |
218 IPC::Channel::CreateNamedServer(IPC::ChannelHandle(pipe.Get()), this); | |
219 CHECK(ipc_server_channel_->Connect()); | |
220 } | |
221 | |
222 void GnubbyAuthHandlerWin::RecreateIpcServerChannel() { | |
223 DCHECK(thread_checker_.CalledOnValidThread()); | |
224 | |
225 timer_.Stop(); | |
226 ipc_server_channel_.reset(); | |
227 | |
228 StartIpcServerChannel(); | |
229 } | |
230 | |
231 void GnubbyAuthHandlerWin::CloseSecurityKeyRequestIpcChannel( | |
232 int connection_id) { | |
233 active_channels_.erase(connection_id); | |
234 } | |
235 | |
236 GnubbyAuthHandlerWin::ActiveChannels::const_iterator | |
237 GnubbyAuthHandlerWin::GetChannelForConnectionId(int connection_id) const { | |
238 return active_channels_.find(connection_id); | |
239 } | |
240 | |
241 std::string GnubbyAuthHandlerWin::GenerateUniqueChannelName() { | |
242 return GetRemoteSecurityKeyIpcChannelName() + "." + | |
243 IPC::Channel::GenerateUniqueRandomChannelID(); | |
244 } | |
245 | |
246 bool GnubbyAuthHandlerWin::OnMessageReceived(const IPC::Message& message) { | |
247 DCHECK(thread_checker_.CalledOnValidThread()); | |
248 // This class does handle any IPC messages sent by the client. | |
249 return false; | |
250 } | |
251 | |
252 void GnubbyAuthHandlerWin::OnChannelConnected(int32_t peer_pid) { | |
253 DCHECK(thread_checker_.CalledOnValidThread()); | |
254 | |
255 timer_.Start(FROM_HERE, disconnect_timeout_, | |
256 base::Bind(&GnubbyAuthHandlerWin::OnChannelError, | |
257 base::Unretained(this))); | |
258 | |
259 // Verify the IPC connection attempt originated from the session we are | |
260 // currently remoting. We don't want to service requests from arbitrary | |
261 // Windows sessions. | |
262 bool close_connection = false; | |
263 DWORD peer_session_id; | |
264 if (!ProcessIdToSessionId(peer_pid, &peer_session_id)) { | |
265 PLOG(ERROR) << "ProcessIdToSessionId() failed"; | |
266 close_connection = true; | |
267 } else if (peer_session_id != client_session_details_->desktop_session_id()) { | |
268 LOG(INFO) << "Ignoring connection attempt from outside remoted session."; | |
269 close_connection = true; | |
270 } | |
271 if (close_connection) { | |
272 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
273 FROM_HERE, base::Bind(&GnubbyAuthHandlerWin::OnChannelError, | |
274 weak_factory_.GetWeakPtr())); | |
275 return; | |
276 } | |
277 | |
278 int new_connection_id = ++last_connection_id_; | |
279 std::unique_ptr<RemoteSecurityKeyIpcServer> ipc_server( | |
280 RemoteSecurityKeyIpcServer::Create( | |
281 new_connection_id, peer_session_id, disconnect_timeout_, | |
282 send_message_callback_, | |
283 base::Bind(&GnubbyAuthHandlerWin::CloseSecurityKeyRequestIpcChannel, | |
284 base::Unretained(this), new_connection_id))); | |
285 | |
286 std::string unique_channel_name = GenerateUniqueChannelName(); | |
287 if (ipc_server->CreateChannel( | |
288 unique_channel_name, | |
289 base::TimeDelta::FromSeconds(kGnubbyRequestTimeoutSeconds))) { | |
290 active_channels_[new_connection_id] = std::move(ipc_server); | |
291 ipc_server_channel_->Send( | |
292 new ChromotingNetworkToRemoteSecurityKeyMsg_ConnectionDetails( | |
293 unique_channel_name)); | |
294 } | |
295 } | |
296 | |
297 void GnubbyAuthHandlerWin::OnChannelError() { | |
298 DCHECK(thread_checker_.CalledOnValidThread()); | |
299 | |
300 // Could be an error, most likely the client disconnected though. Either way | |
301 // we should restart the server to prepare for the next connection. | |
302 RecreateIpcServerChannel(); | |
303 } | |
304 | |
305 } // namespace remoting | |
OLD | NEW |