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_extension_session.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/json/json_reader.h" | |
9 #include "base/json/json_writer.h" | |
10 #include "base/logging.h" | |
11 #include "base/macros.h" | |
12 #include "base/values.h" | |
13 #include "remoting/base/logging.h" | |
14 #include "remoting/host/client_session_details.h" | |
15 #include "remoting/host/security_key/gnubby_auth_handler.h" | |
16 #include "remoting/proto/control.pb.h" | |
17 #include "remoting/protocol/client_stub.h" | |
18 | |
19 namespace { | |
20 | |
21 // Used as the type attribute of all Security Key protocol::ExtensionMessages. | |
22 const char kExtensionMessageType[] = "gnubby-auth"; | |
23 | |
24 // Gnubby extension message data members. | |
25 const char kConnectionId[] = "connectionId"; | |
26 const char kControlMessage[] = "control"; | |
27 const char kControlOption[] = "option"; | |
28 const char kDataMessage[] = "data"; | |
29 const char kDataPayload[] = "data"; | |
30 const char kErrorMessage[] = "error"; | |
31 const char kGnubbyAuthV1[] = "auth-v1"; | |
32 const char kMessageType[] = "type"; | |
33 | |
34 // Returns the command code (the first byte of the data) if it exists, or -1 if | |
35 // the data is empty. | |
36 unsigned int GetCommandCode(const std::string& data) { | |
37 return data.empty() ? -1 : static_cast<unsigned int>(data[0]); | |
38 } | |
39 | |
40 // Creates a string of byte data from a ListValue of numbers. Returns true if | |
41 // all of the list elements are numbers. | |
42 bool ConvertListValueToString(base::ListValue* bytes, std::string* out) { | |
43 out->clear(); | |
44 | |
45 unsigned int byte_count = bytes->GetSize(); | |
46 if (byte_count != 0) { | |
47 out->reserve(byte_count); | |
48 for (unsigned int i = 0; i < byte_count; i++) { | |
49 int value; | |
50 if (!bytes->GetInteger(i, &value)) { | |
51 return false; | |
52 } | |
53 out->push_back(static_cast<char>(value)); | |
54 } | |
55 } | |
56 return true; | |
57 } | |
58 | |
59 } // namespace | |
60 | |
61 namespace remoting { | |
62 | |
63 GnubbyExtensionSession::GnubbyExtensionSession( | |
64 ClientSessionDetails* client_session_details, | |
65 protocol::ClientStub* client_stub) | |
66 : client_stub_(client_stub) { | |
67 DCHECK(client_stub_); | |
68 | |
69 gnubby_auth_handler_ = remoting::GnubbyAuthHandler::Create( | |
70 client_session_details, | |
71 base::Bind(&GnubbyExtensionSession::SendMessageToClient, | |
72 base::Unretained(this))); | |
73 } | |
74 | |
75 GnubbyExtensionSession::~GnubbyExtensionSession() {} | |
76 | |
77 // Returns true if the |message| is a Security Key ExtensionMessage. | |
78 // This is done so the host does not pass |message| to other HostExtensions. | |
79 // TODO(joedow): Use |client_session_details| to disconnect the session if we | |
80 // receive an invalid extension message. | |
81 bool GnubbyExtensionSession::OnExtensionMessage( | |
82 ClientSessionDetails* client_session_details, | |
83 protocol::ClientStub* client_stub, | |
84 const protocol::ExtensionMessage& message) { | |
85 DCHECK(thread_checker_.CalledOnValidThread()); | |
86 | |
87 if (message.type() != kExtensionMessageType) { | |
88 return false; | |
89 } | |
90 | |
91 std::unique_ptr<base::Value> value = base::JSONReader::Read(message.data()); | |
92 base::DictionaryValue* client_message; | |
93 if (!value || !value->GetAsDictionary(&client_message)) { | |
94 LOG(WARNING) << "Failed to retrieve data from gnubby-auth message."; | |
95 return true; | |
96 } | |
97 | |
98 std::string type; | |
99 if (!client_message->GetString(kMessageType, &type)) { | |
100 LOG(WARNING) << "Invalid gnubby-auth message format."; | |
101 return true; | |
102 } | |
103 | |
104 if (type == kControlMessage) { | |
105 ProcessControlMessage(client_message); | |
106 } else if (type == kDataMessage) { | |
107 ProcessDataMessage(client_message); | |
108 } else if (type == kErrorMessage) { | |
109 ProcessErrorMessage(client_message); | |
110 } else { | |
111 VLOG(2) << "Unknown gnubby-auth message type: " << type; | |
112 } | |
113 | |
114 return true; | |
115 } | |
116 | |
117 void GnubbyExtensionSession::ProcessControlMessage( | |
118 base::DictionaryValue* message_data) const { | |
119 std::string option; | |
120 if (!message_data->GetString(kControlOption, &option)) { | |
121 LOG(WARNING) << "Could not extract control option from message."; | |
122 return; | |
123 } | |
124 | |
125 if (option == kGnubbyAuthV1) { | |
126 gnubby_auth_handler_->CreateGnubbyConnection(); | |
127 } else { | |
128 VLOG(2) << "Invalid gnubby-auth control option: " << option; | |
129 } | |
130 } | |
131 | |
132 void GnubbyExtensionSession::ProcessDataMessage( | |
133 base::DictionaryValue* message_data) const { | |
134 int connection_id; | |
135 if (!message_data->GetInteger(kConnectionId, &connection_id)) { | |
136 LOG(WARNING) << "Could not extract connection id from message."; | |
137 return; | |
138 } | |
139 | |
140 if (!gnubby_auth_handler_->IsValidConnectionId(connection_id)) { | |
141 LOG(WARNING) << "Unknown gnubby-auth data connection: '" << connection_id | |
142 << "'"; | |
143 return; | |
144 } | |
145 | |
146 base::ListValue* bytes; | |
147 std::string response; | |
148 if (message_data->GetList(kDataPayload, &bytes) && | |
149 ConvertListValueToString(bytes, &response)) { | |
150 HOST_LOG << "Sending gnubby response: " << GetCommandCode(response); | |
151 gnubby_auth_handler_->SendClientResponse(connection_id, response); | |
152 } else { | |
153 LOG(WARNING) << "Could not extract response data from message."; | |
154 gnubby_auth_handler_->SendErrorAndCloseConnection(connection_id); | |
155 return; | |
156 } | |
157 } | |
158 | |
159 void GnubbyExtensionSession::ProcessErrorMessage( | |
160 base::DictionaryValue* message_data) const { | |
161 int connection_id; | |
162 if (!message_data->GetInteger(kConnectionId, &connection_id)) { | |
163 LOG(WARNING) << "Could not extract connection id from message."; | |
164 return; | |
165 } | |
166 | |
167 if (gnubby_auth_handler_->IsValidConnectionId(connection_id)) { | |
168 HOST_LOG << "Sending gnubby error"; | |
169 gnubby_auth_handler_->SendErrorAndCloseConnection(connection_id); | |
170 } else { | |
171 LOG(WARNING) << "Unknown gnubby-auth data connection: '" << connection_id | |
172 << "'"; | |
173 } | |
174 } | |
175 | |
176 void GnubbyExtensionSession::SendMessageToClient( | |
177 int connection_id, | |
178 const std::string& data) const { | |
179 DCHECK(thread_checker_.CalledOnValidThread()); | |
180 DCHECK(client_stub_); | |
181 | |
182 base::DictionaryValue request; | |
183 request.SetString(kMessageType, kDataMessage); | |
184 request.SetInteger(kConnectionId, connection_id); | |
185 | |
186 std::unique_ptr<base::ListValue> bytes(new base::ListValue()); | |
187 for (std::string::const_iterator i = data.begin(); i != data.end(); ++i) { | |
188 bytes->AppendInteger(static_cast<unsigned char>(*i)); | |
189 } | |
190 request.Set(kDataPayload, bytes.release()); | |
191 | |
192 std::string request_json; | |
193 CHECK(base::JSONWriter::Write(request, &request_json)); | |
194 | |
195 protocol::ExtensionMessage message; | |
196 message.set_type(kExtensionMessageType); | |
197 message.set_data(request_json); | |
198 | |
199 client_stub_->DeliverHostMessage(message); | |
200 } | |
201 | |
202 void GnubbyExtensionSession::SetGnubbyAuthHandlerForTesting( | |
203 std::unique_ptr<GnubbyAuthHandler> gnubby_auth_handler) { | |
204 DCHECK(gnubby_auth_handler); | |
205 | |
206 gnubby_auth_handler_ = std::move(gnubby_auth_handler); | |
207 gnubby_auth_handler_->SetSendMessageCallback(base::Bind( | |
208 &GnubbyExtensionSession::SendMessageToClient, base::Unretained(this))); | |
209 } | |
210 | |
211 } // namespace remoting | |
OLD | NEW |