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 "components/gcm_driver/gcm_driver.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "components/gcm_driver/crypto/gcm_decryption_result.h"
16 #include "components/gcm_driver/crypto/gcm_encryption_result.h"
17 #include "components/gcm_driver/gcm_app_handler.h"
18
19 namespace gcm {
20
21 InstanceIDHandler::InstanceIDHandler() = default;
22
23 InstanceIDHandler::~InstanceIDHandler() = default;
24
DeleteAllTokensForApp(const std::string & app_id,DeleteTokenCallback callback)25 void InstanceIDHandler::DeleteAllTokensForApp(const std::string& app_id,
26 DeleteTokenCallback callback) {
27 DeleteToken(app_id, "*", "*", std::move(callback));
28 }
29
GCMDriver(const base::FilePath & store_path,const scoped_refptr<base::SequencedTaskRunner> & blocking_task_runner)30 GCMDriver::GCMDriver(
31 const base::FilePath& store_path,
32 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) {
33 // The |blocking_task_runner| can be nullptr for tests that do not need the
34 // encryption capabilities of the GCMDriver class.
35 if (blocking_task_runner)
36 encryption_provider_.Init(store_path, blocking_task_runner);
37 }
38
39 GCMDriver::~GCMDriver() = default;
40
Register(const std::string & app_id,const std::vector<std::string> & sender_ids,RegisterCallback callback)41 void GCMDriver::Register(const std::string& app_id,
42 const std::vector<std::string>& sender_ids,
43 RegisterCallback callback) {
44 DCHECK(!app_id.empty());
45 DCHECK(!sender_ids.empty() && sender_ids.size() <= kMaxSenders);
46 DCHECK(!callback.is_null());
47
48 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
49 if (result != GCMClient::SUCCESS) {
50 std::move(callback).Run(std::string(), result);
51 return;
52 }
53
54 // If previous register operation is still in progress, bail out.
55 if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
56 std::move(callback).Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
57 return;
58 }
59
60 // Normalize the sender IDs by making them sorted.
61 std::vector<std::string> normalized_sender_ids = sender_ids;
62 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
63
64 register_callbacks_[app_id] = std::move(callback);
65
66 // If previous unregister operation is still in progress, wait until it
67 // finishes. We don't want to throw ASYNC_OPERATION_PENDING when the user
68 // uninstalls an app (ungistering) and then reinstalls the app again
69 // (registering).
70 auto unregister_iter = unregister_callbacks_.find(app_id);
71 if (unregister_iter != unregister_callbacks_.end()) {
72 // Replace the original unregister callback with an intermediate callback
73 // that will invoke the original unregister callback and trigger the pending
74 // registration after the unregistration finishes.
75 // Note that some parameters to RegisterAfterUnregister are specified here
76 // when the callback is created (base::Bind supports the partial binding
77 // of parameters).
78 unregister_iter->second = base::BindOnce(
79 &GCMDriver::RegisterAfterUnregister, weak_ptr_factory_.GetWeakPtr(),
80 app_id, normalized_sender_ids, std::move(unregister_iter->second));
81 return;
82 }
83
84 RegisterImpl(app_id, normalized_sender_ids);
85 }
86
Unregister(const std::string & app_id,UnregisterCallback callback)87 void GCMDriver::Unregister(const std::string& app_id,
88 UnregisterCallback callback) {
89 UnregisterInternal(app_id, nullptr /* sender_id */, std::move(callback));
90 }
91
UnregisterWithSenderId(const std::string & app_id,const std::string & sender_id,UnregisterCallback callback)92 void GCMDriver::UnregisterWithSenderId(const std::string& app_id,
93 const std::string& sender_id,
94 UnregisterCallback callback) {
95 DCHECK(!sender_id.empty());
96 UnregisterInternal(app_id, &sender_id, std::move(callback));
97 }
98
UnregisterInternal(const std::string & app_id,const std::string * sender_id,UnregisterCallback callback)99 void GCMDriver::UnregisterInternal(const std::string& app_id,
100 const std::string* sender_id,
101 UnregisterCallback callback) {
102 DCHECK(!app_id.empty());
103 DCHECK(!callback.is_null());
104
105 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
106 if (result != GCMClient::SUCCESS) {
107 std::move(callback).Run(result);
108 return;
109 }
110
111 // If previous un/register operation is still in progress, bail out.
112 if (register_callbacks_.find(app_id) != register_callbacks_.end() ||
113 unregister_callbacks_.find(app_id) != unregister_callbacks_.end()) {
114 std::move(callback).Run(GCMClient::ASYNC_OPERATION_PENDING);
115 return;
116 }
117
118 unregister_callbacks_[app_id] = std::move(callback);
119
120 if (sender_id)
121 UnregisterWithSenderIdImpl(app_id, *sender_id);
122 else
123 UnregisterImpl(app_id);
124 }
125
Send(const std::string & app_id,const std::string & receiver_id,const OutgoingMessage & message,SendCallback callback)126 void GCMDriver::Send(const std::string& app_id,
127 const std::string& receiver_id,
128 const OutgoingMessage& message,
129 SendCallback callback) {
130 DCHECK(!app_id.empty());
131 DCHECK(!receiver_id.empty());
132 DCHECK(!callback.is_null());
133
134 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
135 if (result != GCMClient::SUCCESS) {
136 std::move(callback).Run(std::string(), result);
137 return;
138 }
139
140 // If the message with send ID is still in progress, bail out.
141 std::pair<std::string, std::string> key(app_id, message.id);
142 if (send_callbacks_.find(key) != send_callbacks_.end()) {
143 std::move(callback).Run(message.id, GCMClient::INVALID_PARAMETER);
144 return;
145 }
146
147 send_callbacks_[key] = std::move(callback);
148
149 SendImpl(app_id, receiver_id, message);
150 }
151
GetEncryptionInfo(const std::string & app_id,GetEncryptionInfoCallback callback)152 void GCMDriver::GetEncryptionInfo(const std::string& app_id,
153 GetEncryptionInfoCallback callback) {
154 encryption_provider_.GetEncryptionInfo(app_id, "" /* authorized_entity */,
155 std::move(callback));
156 }
157
UnregisterWithSenderIdImpl(const std::string & app_id,const std::string & sender_id)158 void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
159 const std::string& sender_id) {
160 NOTREACHED();
161 }
162
RegisterFinished(const std::string & app_id,const std::string & registration_id,GCMClient::Result result)163 void GCMDriver::RegisterFinished(const std::string& app_id,
164 const std::string& registration_id,
165 GCMClient::Result result) {
166 auto callback_iter = register_callbacks_.find(app_id);
167 if (callback_iter == register_callbacks_.end()) {
168 // The callback could have been removed when the app is uninstalled.
169 return;
170 }
171
172 RegisterCallback callback = std::move(callback_iter->second);
173 register_callbacks_.erase(callback_iter);
174 std::move(callback).Run(registration_id, result);
175 }
176
RemoveEncryptionInfoAfterUnregister(const std::string & app_id,GCMClient::Result result)177 void GCMDriver::RemoveEncryptionInfoAfterUnregister(const std::string& app_id,
178 GCMClient::Result result) {
179 encryption_provider_.RemoveEncryptionInfo(
180 app_id, "" /* authorized_entity */,
181 base::BindOnce(&GCMDriver::UnregisterFinished,
182 weak_ptr_factory_.GetWeakPtr(), app_id, result));
183 }
184
UnregisterFinished(const std::string & app_id,GCMClient::Result result)185 void GCMDriver::UnregisterFinished(const std::string& app_id,
186 GCMClient::Result result) {
187 auto callback_iter = unregister_callbacks_.find(app_id);
188 if (callback_iter == unregister_callbacks_.end())
189 return;
190
191 UnregisterCallback callback = std::move(callback_iter->second);
192 unregister_callbacks_.erase(callback_iter);
193 std::move(callback).Run(result);
194 }
195
SendFinished(const std::string & app_id,const std::string & message_id,GCMClient::Result result)196 void GCMDriver::SendFinished(const std::string& app_id,
197 const std::string& message_id,
198 GCMClient::Result result) {
199 auto callback_iter = send_callbacks_.find(
200 std::pair<std::string, std::string>(app_id, message_id));
201 if (callback_iter == send_callbacks_.end()) {
202 // The callback could have been removed when the app is uninstalled.
203 return;
204 }
205
206 SendCallback callback = std::move(callback_iter->second);
207 send_callbacks_.erase(callback_iter);
208 std::move(callback).Run(message_id, result);
209 }
210
Shutdown()211 void GCMDriver::Shutdown() {
212 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
213 iter != app_handlers_.end(); ++iter) {
214 DVLOG(1) << "Calling ShutdownHandler for: " << iter->first;
215 iter->second->ShutdownHandler();
216 }
217 app_handlers_.clear();
218 }
219
AddAppHandler(const std::string & app_id,GCMAppHandler * handler)220 void GCMDriver::AddAppHandler(const std::string& app_id,
221 GCMAppHandler* handler) {
222 DCHECK(!app_id.empty());
223 DCHECK(handler);
224 DCHECK_EQ(app_handlers_.count(app_id), 0u);
225 app_handlers_[app_id] = handler;
226 DVLOG(1) << "App handler added for: " << app_id;
227 }
228
RemoveAppHandler(const std::string & app_id)229 void GCMDriver::RemoveAppHandler(const std::string& app_id) {
230 DCHECK(!app_id.empty());
231 app_handlers_.erase(app_id);
232 DVLOG(1) << "App handler removed for: " << app_id;
233 }
234
GetAppHandler(const std::string & app_id)235 GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
236 // Look for exact match.
237 GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
238 if (iter != app_handlers_.end())
239 return iter->second;
240
241 // Ask the handlers whether they know how to handle it.
242 for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
243 if (iter->second->CanHandle(app_id))
244 return iter->second;
245 }
246
247 return nullptr;
248 }
249
GetEncryptionProviderInternal()250 GCMEncryptionProvider* GCMDriver::GetEncryptionProviderInternal() {
251 return &encryption_provider_;
252 }
253
HasRegisterCallback(const std::string & app_id)254 bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
255 return register_callbacks_.find(app_id) != register_callbacks_.end();
256 }
257
ClearCallbacks()258 void GCMDriver::ClearCallbacks() {
259 register_callbacks_.clear();
260 unregister_callbacks_.clear();
261 send_callbacks_.clear();
262 }
263
DispatchMessage(const std::string & app_id,const IncomingMessage & message)264 void GCMDriver::DispatchMessage(const std::string& app_id,
265 const IncomingMessage& message) {
266 encryption_provider_.DecryptMessage(
267 app_id, message,
268 base::BindOnce(&GCMDriver::DispatchMessageInternal,
269 weak_ptr_factory_.GetWeakPtr(), app_id));
270 }
271
DispatchMessageInternal(const std::string & app_id,GCMDecryptionResult result,IncomingMessage message)272 void GCMDriver::DispatchMessageInternal(const std::string& app_id,
273 GCMDecryptionResult result,
274 IncomingMessage message) {
275 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
276 GCMDecryptionResult::ENUM_SIZE);
277
278 switch (result) {
279 case GCMDecryptionResult::UNENCRYPTED:
280 case GCMDecryptionResult::DECRYPTED_DRAFT_03:
281 case GCMDecryptionResult::DECRYPTED_DRAFT_08: {
282 GCMAppHandler* handler = GetAppHandler(app_id);
283 UMA_HISTOGRAM_BOOLEAN("GCM.DeliveredToAppHandler", !!handler);
284
285 if (handler)
286 handler->OnMessage(app_id, message);
287
288 // TODO(peter/harkness): Surface unavailable app handlers on
289 // chrome://gcm-internals and send a delivery receipt.
290 return;
291 }
292 case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
293 case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
294 case GCMDecryptionResult::NO_KEYS:
295 case GCMDecryptionResult::INVALID_SHARED_SECRET:
296 case GCMDecryptionResult::INVALID_PAYLOAD:
297 case GCMDecryptionResult::INVALID_BINARY_HEADER_PAYLOAD_LENGTH:
298 case GCMDecryptionResult::INVALID_BINARY_HEADER_RECORD_SIZE:
299 case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_LENGTH:
300 case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_FORMAT: {
301 RecordDecryptionFailure(app_id, result);
302 GCMAppHandler* handler = GetAppHandler(app_id);
303 if (handler) {
304 handler->OnMessageDecryptionFailed(
305 app_id, message.message_id,
306 ToGCMDecryptionResultDetailsString(result));
307 }
308 return;
309 }
310 case GCMDecryptionResult::ENUM_SIZE:
311 break; // deliberate fall-through
312 }
313
314 NOTREACHED();
315 }
316
RegisterAfterUnregister(const std::string & app_id,const std::vector<std::string> & normalized_sender_ids,UnregisterCallback unregister_callback,GCMClient::Result result)317 void GCMDriver::RegisterAfterUnregister(
318 const std::string& app_id,
319 const std::vector<std::string>& normalized_sender_ids,
320 UnregisterCallback unregister_callback,
321 GCMClient::Result result) {
322 // Invoke the original unregister callback.
323 std::move(unregister_callback).Run(result);
324
325 // Trigger the pending registration.
326 DCHECK(register_callbacks_.find(app_id) != register_callbacks_.end());
327 RegisterImpl(app_id, normalized_sender_ids);
328 }
329
EncryptMessage(const std::string & app_id,const std::string & authorized_entity,const std::string & p256dh,const std::string & auth_secret,const std::string & message,EncryptMessageCallback callback)330 void GCMDriver::EncryptMessage(const std::string& app_id,
331 const std::string& authorized_entity,
332 const std::string& p256dh,
333 const std::string& auth_secret,
334 const std::string& message,
335 EncryptMessageCallback callback) {
336 encryption_provider_.EncryptMessage(
337 app_id, authorized_entity, p256dh, auth_secret, message,
338 base::BindOnce(&GCMDriver::OnMessageEncrypted,
339 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
340 }
341
OnMessageEncrypted(EncryptMessageCallback callback,GCMEncryptionResult result,std::string message)342 void GCMDriver::OnMessageEncrypted(EncryptMessageCallback callback,
343 GCMEncryptionResult result,
344 std::string message) {
345 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.EncryptMessageResult", result,
346 GCMEncryptionResult::ENUM_SIZE);
347 std::move(callback).Run(result, std::move(message));
348 }
349
DecryptMessage(const std::string & app_id,const std::string & authorized_entity,const std::string & message,DecryptMessageCallback callback)350 void GCMDriver::DecryptMessage(const std::string& app_id,
351 const std::string& authorized_entity,
352 const std::string& message,
353 DecryptMessageCallback callback) {
354 IncomingMessage incoming_message;
355 incoming_message.sender_id = authorized_entity;
356 incoming_message.raw_data = message;
357 incoming_message.data[GCMEncryptionProvider::kContentEncodingProperty] =
358 GCMEncryptionProvider::kContentCodingAes128Gcm;
359 encryption_provider_.DecryptMessage(
360 app_id, incoming_message,
361 base::BindOnce(&GCMDriver::OnMessageDecrypted,
362 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
363 }
364
OnMessageDecrypted(DecryptMessageCallback callback,GCMDecryptionResult result,IncomingMessage message)365 void GCMDriver::OnMessageDecrypted(DecryptMessageCallback callback,
366 GCMDecryptionResult result,
367 IncomingMessage message) {
368 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
369 GCMDecryptionResult::ENUM_SIZE);
370 std::move(callback).Run(result, std::move(message.raw_data));
371 }
372
373 } // namespace gcm
374